2015-10-21 10 views
8

Widziałem następujący kod:C++ sposób identyczny podpis ale inny typ zwracany

template <class T> 
class Type { 
    public: 
     Type() {} 
     T& operator=(const T& rhs) {value() = rhs; return value();} 
     T& value() {return m_value;} 
     T value() const {return m_value;} 
    private: 
     T m_value; 
}; 

Dlaczego nie kompilator narzekać

T& value() {return m_value;} 
    T value() const {return m_value;} 

i jak wiesz, który z nich jest wywoływana?

+1

Nie ignoruj ​​słów kluczowych podczas określania, czy dwa sygnatury funkcji są "identyczne"! –

Odpowiedz

13

Obie funkcje w rzeczywistości nie są takie same. Tylko druga funkcja jest zadeklarowana jako funkcja składowa const. Jeśli obiekt, z którego wywoływany jest element, to const, używana jest ta ostatnia opcja. Jeśli obiekt jest inny niż const, używana jest pierwsza opcja.

przykład:

void any_func(const Type *t) 
{ 
    something = t->value(); //second `const` version used 
} 

void any_func2(Type *t) 
{ 
    something = t->value(); //first non-`const` version used 
} 

Jeżeli obie grupy uznano za non const lub obie zostały uznane const kompilator będzie (powinny w każdym razie) skarżą.

7

Dlaczego nie kompilator narzekać

Ponieważ const liczy innego podpisu funkcji. Twoje założenie, że podpisy funkcji są identyczne jest nieprawidłowe.
Funkcja oznaczona const zostanie wywołana dla dowolnej instancji const lub odwołania do.

i jak się dowiedzieć, który z nich jest wywoływany?

Umieść cout oświadczenie w funkcjach i przetestować następujące przypadki:

template <class T> 
class Type { 
    public: 
     Type() {} 
     T& operator=(const T& rhs) {value() = rhs; return value();} 
     T& value() { 
      std::cout << "non const version" << std endl; 
      return m_value; 
     } 
     T value() const { 
      std::cout << "const version" << std endl; 
      return m_value; 
     } 
    private: 
     T m_value; 
}; 

int main() { 
    Type<int> t; 
    t.value(); 

    Type<int> rt = t; 
    rt.value(); 

    Type<int>* pt = &t; 
    pt->value(); 

    const Type<int> ct; 
    ct.value(); 

    const Type<int>& crt = t; 
    crt.value(); 

    const Type<int>* pct = &t; 
    pct->value(); 
} 

Operator przypisania wezwie niepodlegania wersję const.


Wersja const powinien lepiej wyglądać

const T& value() const { 
    std::cout << "const version" << std endl; 
    return m_value; 
} 

ponieważ nie zawsze można polegać na RVO (optymalizacja wartości zwracanej) oraz dodatkowe kopie mogą być podjęte (szczególnie dla starszych implementacji kompilatora).


pamiętać, że operator przypisania powinien zwrócić odwołanie do bieżącej instancji również:

Type& operator=(const T& rhs) {value() = rhs; return *this;} 
5

Kilka słów na priorytetu rozdzielczości funkcje. Kompilator rozróżnia między funkcjami const/non const w następujący sposób:

Jeśli klasa ma tylko funkcję const o podanej nazwie i liście argumentów, zostanie wywołana dla obiektów stałych i niestałych, podobnie.Po wywołaniu tej funkcji obiekt "przyjmie" constness (nawet jeśli nie był const), co oznacza, że ​​funkcja może wywoływać tylko inne funkcje const.

Jeśli klasa ma tylko funkcję inną niż stała, zostanie wywołana dla obiektów niestanowiących stałych. Próba wywołania tej funkcji dla obiektów const spowoduje błąd kompilacji.

Jeśli klasa ma obie dostępne funkcje, dla obiektów stałych będzie używana wersja const, a dla obiektów niestanowiących stałych używana będzie wersja stała.

Dzięki @owacoder za zwrócenie mojej uwagi na początkowe miksowanie w opisie.

+3

Myślę, że logika w drugim akapicie jest wstecz. Funkcje inne niż 'konstancja ** ** nie mogą być wywoływane dla obiektów' const'. – owacoder

+0

@owacoder, oczywiście, że mogą. Spróbuj sam. – SergeyA

+0

@owacoder, moje złe. Masz rację, źle odczytałem twój komentarz i źle wpisałem odpowiedź. Dokona edycji. – SergeyA

Powiązane problemy