2017-10-14 104 views
7

Mam klasę C, która ma dowolną operację castingu. W tym przykładzie spróbowałem rzucić jego instancję na std::string na trzy różne sposoby: static_cast, konstruktor i przypisanie do std::string. Jednak tylko ostatni kompiluje, podczas gdy inni podnoszą błąd niejednoznacznego konstruktora.Różne zachowania między jawną rzutowaniem, bezpośrednią inicjalizacją i inicjalizacją kopiowania

Powód błędu jest wystarczająco jasny: istnieje wiele sposobów konwersji C na coś, co może zaakceptować konstruktor std::string. Ale jaka jest różnica między tymi przypadkami? Dlaczego operator cast działa tak, jak powinien, ale nie?

struct C { 
    template<typename T> 
    operator T() const { 
     return T{}; 
    } 
}; 

int main() { 
    C c; 

    cout << static_cast<string>(c) << endl; // compile error 
    string bad(c); // compile error 
    string ok = c; // compiles successfully 
} 

UPD: jak bolov mowa w komentarzach, ten problem nie odtwarza z C++ 17. Przetestowałem go za pomocą g ++ - 5 i clang-3.8 z opcjami -std = C++ 11 i -std = C++ 14, i pokazuje on opisane błędy.

+0

nie można odtworzyć: https://godbolt.org/g/ESR8cw – bolov

+0

dziwo @bolov, ale nie powielać w C++ 17. Powtarza się przy godbolt z opcją -std = C++ 11. Dodam go do postu, dzięki. –

+0

hmm .. ciekawe – bolov

Odpowiedz

6

czym C++ 17

static_cast<string>(c) i string bad(c) preform direct initialization, następnie

konstruktorzy T są badane, a najlepiej pasujące jest wybrany rozmiar przeciążenia. Następnie wywoływany jest konstruktor w celu zainicjowania obiektu.

jak pan powiedział, wszystkie możliwe konstruktorzy std::string są badane i C mogą być konwertowane do niczego wymagane, a następnie powoduje niejasności.

string ok = c wykonuje copy initialization (należy zauważyć, że nie jest przydział), wówczas

Jeśli T to typ klasy, a wersja CV bez zastrzeżeń od rodzaju other nie T lub pochodną T, czy T nie jest typem klasy, ale typ other jest typem użytkownika, zdefiniowanym przez użytkownika sekwencją konwersji, która może zostać przekształcona z typu other na T (lub na typ pochodzący od T, jeśli T jest typem klasy, a funkcja konwersji jest dostępne) są badane Najlepszą z nich wybiera się poprzez rozdzielczość przeciążania.

Oznacza to, że konwersja z C do std::string jest sprawdzana i używana do inicjalizacji tutaj.

Po C++ 17

ponieważ C++ 17 dla direct initlizatioin,

gdy inicjator jest prvalue ekspresja którego CV niewykwalifikowany typ jest tego samego rodzaju co T, inicjatora samo wyrażenie, a raczej tymczasowy zmaterializowany z niego, jest używane do zainicjowania obiektu docelowego: patrz (od C++ 17)

Oznacza to, że konwersja z C na std::string jest określona i używana do inicjowania, a następnie niejasność znika, a kod działa dobrze.

LIVE

+1

Aby opracować procedurę inicjowania kopiowania ([over.ics.user]/3): * Jeśli konwersja zdefiniowana przez użytkownika jest określona przez specjalizację szablonu funkcji konwersji, druga standardowa sekwencja konwersji musi mieć dokładny stopień dopasowania. * Nie powiedziałbym, że jest tak bardzo pożądany, jak jest to wymagane. – chris