2014-10-25 10 views
15

Następujący minimalny kod kompiluje na g ++, ale nie będzie kompilować na brzękiem ++:Czy ten kod jest prawidłowy? Współpracuje z gcc, nie działają z brzękiem

template<class T> 
T operator*(float a, const T& b) 
{ 
    return b * a; 
} 

struct A{ 
    A operator*(float b) const 
    { 
     A a; 
     return a; 
    } 
}; 

int main() 
{ 
    A a; 
    2.0f * a; 
} 

Jest to błąd otrzymuję:

$ clang++ test.cpp 
test.cpp:2:3: error: overloaded 'operator*' must have at least one parameter of 
     class or enumeration type 
T operator*(float a, const T& b) 
^
test.cpp:4:11: note: in instantiation of function template specialization 
     'operator*<float>' requested here 
     return b * a; 
       ^
test.cpp:18:10: note: in instantiation of function template specialization 
     'operator*<A>' requested here 
    2.0f * a; 
     ^
1 error generated. 

Clang wersja 3.5. Czy ten kod jest prawidłowy? Czy na Clangu jest błąd?

+5

http://stackoverflow.com/questions/18596412/strange-error-with-a-templated-operator-overload Może pomóc? – IllusiveBrian

+2

Myślę, że @Namfuak znalazł duplikat: wyrażenie 'b * a' próbuje utworzyć szablon funkcji operatora, który prowadzi do' T' wydedukowanego jako 'float', a wytworzona specjalizacja (funkcja operatora) jest niedopuszczalna uformowany, ponieważ nie ma parametru typu klasy/wyliczenia lub jego odwołania. Wygląda na to, że nie występuje awaria substytucji, tj. Poważny błąd. Zobacz [powyżej.oper]/1 i/6. – dyp

+0

Próbowałem dalej minimalizować twój kod i usunięto 'A :: operator *' ... ale zauważyłem, że to uniemożliwiło GCC skompilowanie kodu. Interesujące ... –

Odpowiedz

2

tworzy instancję ::operator*<A>. W ramach tej funkcji mamy wyrażenie: b * a, które, jeśli spojrzysz na typy (uproszczone), to A * float. W tym momencie kompilator musi dokonać wyboru. Czy ta * powinna być globalną funkcją ::operator*<float> (ponieważ argumentem po prawej stronie jest float), czy powinien to być A::operator*? Dla nas, ludzi, jasne jest, że powinno to być A::operator*, ale z punktu widzenia kompilatora nie jest to od razu jasne.

Co robi kompilator? Najpierw próbuje znaleźć wszystkie funkcje operator*, które mogą być używane (po czym próbuje dokładnie określić, którego użyć). Jedną z funkcji operator*, którą można zastosować, jest ::operator*<float>. Ale czekaj, co to jest ::operator*<float>? To jest float *(float, const float&)! I nie możemy tego zrobić! Nie można przeciążać operatorów dla typów pierwotnych (wyobraź sobie chaos, jeśli przeładowałeś int +(int, int), więc robisz coś zupełnie innego niż to, co wszyscy oczekiwali).

W tym momencie program jest źle sformułowany. Sam fakt, że kompilator próbuje nawet utworzyć instancję ::operator*<float>, unieważnia program jako całość. Więc co możemy zrobić? Poinformować kompilator dokładnie, co robić:

template<class T> 
T operator*(float a, const T& b) 
{ 
    // This prevents the compiler from instantiating ::operator*<float> 
    return b.operator*(a); 

    // The above is meant to illustrate how the fix needs to work: it needs 
    // to avoid instantiating ::operator*<float>. Other methods can be used 
    // (like SFINAE) that might be more elegant (check out Walter's answer 
    // in the duplicate: https://stackoverflow.com/a/18596809/1287251), but 
    // in the end any solution used must avoid ::operator*<float>. 
} 

struct A{ 
    A operator*(float b) const 
    { 
     A a; 
     return a; 
    } 
}; 

int main() 
{ 
    A a; 
    2.0f * a; 
} 

W skrócie, aby odpowiedzieć na pytanie: nie, kod nie jest prawidłowy. Musisz uniemożliwić kompilatorowi próbę utworzenia instancji ::operator*<float>.

This is explained by @dyp in the comments i by @TemplateRex in the duplicate question. Jednak musiałem przeczytać ich odpowiedzi kilka razy, zanim zrozumiałem, co mają na myśli. Próbowałem uprościć rzeczy w tej odpowiedzi. Jeśli mogę to poprawić, proszę dać mi znać!