2012-10-15 11 views
7

Widziałem inne pytania na temat SO dotyczące tego, ale żaden nie wyjaśnia tego w całości. Co to jest kompilator dla obsługi dwóch poniższych sytuacji? Próbowałem go z gcc 4.7.1 (z -std = C++ 0x), VS2010 i VS2012 uzyskać różne rezultaty na wszystkich:Operator konwersji niepotrzebny przeciążenie, kompilatory różnią się

Przykład 1:

struct BB 
{ 
// generic cast 
template<typename T> 
operator T() const 
{ 
    return 0; 
} 

// string cast 
operator std::string() const 
{ 
    return string("hello"); 
} 
}; 

int main() 
{ 
    BB b; 
    string s = b; 
} 

wyjściowa:

  • gcc 4.7.1: Ok
  • VS2010: Ok
  • VS2012: Fail: "nie można przekonwertować z BB do string"

Przykład 2:

struct BB 
{ 
// generic cast 
template<typename T> 
operator T() const 
{ 
    return 0; 
} 

// string cast 
operator std::string() const 
{ 
    return string("hello"); 
} 
}; 

int main() 
{ 
    BB b; 
    string s = (string)b; 

wyjściowa:

  • GCC 4.7.1: ustała wywołanie przeciążony łańcucha (BB &) jest niejednoznaczną
  • VS2010: OK
  • VS2012: Błąd: "Nie można przekonwertować z BB na ciąg"

Odpowiedz

4

Twoja druga wersja z obsadą w stylu C jest niejednoznaczna. Problem polega na tym, że istnieją dwa sposoby przekształcania instancji klasyw ciąg znaków. Oczywistą ścieżką jest użycie operatora przesyłania std :: string bez szablonu, aby konwertować bezpośrednio na ciąg znaków. Ścieżka jest bardziej przebiegła i wszystkie, prócz VS2010, znalazły ją. Ta druga ścieżka to użycie operatora odrzutu szablonu do przekształcenia BB w alokator dla ciągu znaków, a następnie skonstruowania pustego łańcucha przy użyciu tego przydziału.

Operatorzy odlewania szablonów to potencjalnie niebezpieczne bestie. Właśnie dostarczyłeś kompilatorowi narzędzia do konwertowania BB na dowolną wartość.

Twoja pierwsza wersja ucieka przed tym problemem, ponieważ std::basic_string(const _Alloc& __a) jest jawnym konstruktorem. Jawne konstruktory mogą być używane przez jawne konwersje, ale nie przez niejawne. To jest ten konstruktor i konwersja do alokatora, który tworzy niejednoznaczność, a ta ścieżka nie może być używana z niejawną konwersją.

Co do przyczyny niepowodzenia VS1012 w domniemanej konwersji, może to być błąd w VS2012, lub może być tak, że C++ 11 tworzy jeszcze więcej dróg, aby uzyskać od BB do std::string. Nie jestem ekspertem C++ 11. Nie jestem nawet nowicjuszem.

Jeszcze jedna pozycja: Twój kod będzie nie udało się jeszcze bardziej żałośnie nie użyłeś

std::string s; 
s = b; 

Operator przypisania w połączeniu z operatorem konwersji szablon tworzy więcej sposobów, aby dostać się z b do s. Czy system powinien przekonwertować b na std::string, na char lub char*?

Podsumowanie: Naprawdę powinieneś przemyśleć użycie operatorów konwersji szablonów.

+0

Używam ogólnej konwersji, ponieważ wdrażam coś podobnego doładowania :: dowolne, ale może jest lepszy sposób. Zaznacza to jako odpowiedź, że mksvetkov był dość podobny. – Rolle

2

Powodem niepowodzenia niektórych kompilatorów jest to, że próbują dowiedzieć się, co robisz. Winowajcą jest szablonowe wywołanie operatora.

Jak this SO question wyjaśnia, szablonowe operatorzy muszą być wywołana jawnie (b.operator()<std::string> jeśli chciał nazwać template<typename T> operator();), ale nie ma sposobu, aby połączyć się z szablonie operatora:

b.operator std::string()<std::string>; //invalid 
b.operator std::string <std::string>(); //also invalid, string takes no template arguments 
// a bunch more weird syntax constructions... 

Problem wynika z faktu, że nazwa po operator zależy od argumentu szablonu, więc nie ma sposobu na jej podanie. gcc i VS2012 zorientowali się, co robisz, i zauważyli, że mogą dopasować konwersję do szablonu lub do jednoznacznie zdefiniowanego jednoznacznie>> niejednoznacznego połączenia. VS2010 tego nie zrobił i dzwoni do jednego z nich, można go znaleźć poprzez debugowanie.

Szablon specjalizacji mógłby pomogliśmy w takim wypadku jednak, starając się określić

// string cast 
template<> 
operator std::string() const 
{ 
    return string("hello"); 
} 

zawiedzie, ponieważ kompilator nie może odróżnić tej funkcji i regularnej jednym bez template<> . Gdyby prototypy zawierały pewne argumenty, zadziałałoby, ale operator typename ma dowolne ...

Krótka historia - unikaj operatorów konwersji szablonowych.

+0

Ciągle nie widzę różnicy od przykładu 1 i 2. W obu przypadkach rzut na ciąg musi być zrobiony, prawda, bo operator przypisywania ciągów nie zna BB? Ponadto, tylko gcc wydaje się traktować przykłady różne – Rolle

+0

W pierwszym przypadku ('b.operator std :: string() ;') próbujesz wywołać funkcję 'template operator std :: string();' z argumentem 'std :: string', aw drugim przypadku próbujesz wywołać' operator std :: string () ', ale std :: string nie przyjmuje argumentu szablonu. – mtsvetkov

+1

@mtsvetkov - To nie jest przyczyną różnicy. Różnica polega na tym, że jawni konstruktorzy konwersji nie są uprawnieni do domniemanych konwersji. Pierwszy przykład to domniemana konwersja, druga - jawna. –

Powiązane problemy