2013-06-16 19 views
19

Pracując z przeciążeniami funkcyjnych Ref-wykwalifikowanych, Dostaję różne wyniki od GCC (4.8.1) i dzyń (2,9 i tułowia). Rozważmy następujący kod:rozdzielczości Przeciążenie z Ref-kwalifikatorów

#include <iostream> 
#include <utility> 

struct foo 
{ 
    int& bar() & 
    { 
     std::cout << "non-const lvalue" << std::endl; 
     return _bar; 
    } 
    //~ int&& bar() && 
    //~ { 
    //~  std::cout << "non-const rvalue" << std::endl; 
    //~  return std::move(_bar); 
    //~ } 
    int const& bar() const & 
    { 
     std::cout << "const lvalue" << std::endl; 
     return _bar; 
    } 
    int const&& bar() const && 
    { 
     std::cout << "const rvalue" << std::endl; 
     return std::move(_bar); 
    } 

    int _bar; 
}; 

int main(int argc, char** argv) 
{ 
    foo().bar(); 
} 

Clang kompiluje go i wyjść "const rvalue", natomiast GCC myśli, że to jest niejednoznaczne wywołanie z dwóch wykwalifikowanych const funkcji obie będące najlepszym realne kandydatów. Jeśli dostarczę wszystkie 4 przeciążenia, oba kompilatory wypiszą "non-const rvalue".

Chciałbym wiedzieć, który kompilator - jeśli jakikolwiek - robi dobrą rzecz i jakie są odpowiednie standardowe elementy w grze.

Uwaga: Powodem jest rzeczywiście ważne jest to, że prawdziwy kod deklaruje obie funkcje const zakwalifikowanych jako constexpr. Oczywiście nie ma wyjścia do std::cout i static_cast jest używane zamiast std::move, więc są one ważne definicje constexpr. A ponieważ w C++ 11 nadal implikuje const, nie można zapewnić przeciążenia skomentowanego w przykładowym kodzie, ponieważ spowodowałoby to przedefiniowanie przeciążenia o wartości stałej rownoważnej.

Odpowiedz

27

Po pierwsze, domniemane parametrów obiektów jest traktowany jako normalny, jako parametr za 13.3.1.4:

Na niestatycznych funkcji składowych, typ niejawnego parametru obiektu jest

- „lwartość odniesienie do cv X”dla funkcji zadeklarowanych bez ref-kwalifikacyjnym lub z & ref-kwalifikacyjnym

-«rvalue odniesieniu do cv X»dla funkcji zadeklarowanych z ref-kwalifikacyjnym & &

gdzie X to klasa, której funkcją jest członek, a cv to kwalifikacja cv na deklaracji funkcji członka .

Więc o co prosicie jest równoważna następującej:

void bar(foo&); 
void bar(foo&&); 
void bar(const foo&); 
void bar(const foo&&); 

int main() 
{ 
    bar(foo()); 
} 

Wyrażenie foo() jest prvalue klasa.

Po drugie, niestanowiąca odniesienia wersja o różnej wartości l nie jest możliwa, ponieważ prvalue nie może jej powiązać.

To pozostawia nam trzy funkcje, które można wykorzystać do zabezpieczenia przed przeciążeniem.

Każdy ma jeden parametr obiektu niejawnego (const foo&, foo&& lub const foo&&), więc musimy uszeregować te trzy, aby określić najlepsze dopasowanie.

We wszystkich trzech przypadkach jest to powiązanie referencyjne bezpośrednio związane z. Jest to opisane w deklaratorach/inicjalizacji (8.5.3).

Uszeregowanie trzech możliwych wiązań (const foo&, foo&& i const foo&&) opisano w 13.3.3.2.3:

sekwencji konwersji standardowe S1 lepszą konwersję niż kolejność standardową sekwencję konwersji S2 czy

  • S1 i S2 wiązania odniesienia i nie odnosi się do utajonego parametrów obiektu dla niestatycznych zadeklarowanej funkcji użytkownik bez rEF-kwalifikacjach [wyjątek ten nie dotyczy tutaj wszystkie mają ref-kwalifikatory] i S1 wiąże referencję rvalue do rvalue [prvalue klasy jest wartością rinalną] , a S2 wiąże referencję lvalue.

Oznacza to, że zarówno foo&& i const foo&& są lepsze niż const foo&.

  • S1 i S2 wiązania odniesienia, i typu, do których odniesienia odnoszą są tego samego typu, z wyjątkiem najwyższego poziomu CV kwalifikacyjnych, i typu, w którym referencyjny inicjowane przez S2 dotyczy bardziej cv-kwalifikowany niż typ, którego odniesienie zainicjowane przez S1 oznacza.

Oznacza to, że foo&& jest lepsza niż const foo&&.

Więc Clang ma rację, i jest to błąd w GCC. Przeciążenie rankingu foo().bar() jest następujący:

struct foo 
{ 
    int&& bar() &&;    // VIABLE - BEST (1) 
    int const&& bar() const &&; // VIABLE -  (2) 
    int const& bar() const &; // VIABLE - WORST (3) 
    int& bar() &;    // NOT VIABLE 

    int _bar; 
}; 

Błąd w GCC wydaje się mieć zastosowanie wyłącznie do ukrytych parametrów obiektu (z ref-qualifiers), na normalnym parametru wydaje się, aby uzyskać pozycję poprawne, przynajmniej w punkcie 4.7. 2.

+0

Wartość odniesienia rvalue lepiej pasuje we wszystkich przypadkach. Prawdopodobnie jest to błąd związany z refart kwalifikatorami GGC. – Xeo

+0

Również, [ta odpowiedź] (http://stackoverflow.com/a/8610728/500104) może być istotna. ;) – Xeo

+0

Dziękujemy za szczegółową odpowiedź. Żałuję, że nie mogłem tego zrobić więcej niż raz! –