2012-12-17 17 views
7

W poniższym przykładzie Foo nie działa zgodnie z przeznaczeniem, ale nie mogę zrozumieć, dlaczego jest to możliwe do kompilacji.Używanie referencji jako szablonu typu

#include <string> 
#include <iostream> 

typedef std::string& T; 

T Foo(int & i) 
{ 
    return T(i); 
} 

int main() 
{ 
    int a = 1; 
    std::string & s = Foo(a); 
} 

odkryłem to z szablonów, ale typedef pokazuje, że jego niezwiązane z szablonów. Nie trzeba dodawać, że s nie jest tutaj prawidłowym ciągiem. Myślę, że skonstruowanie wartości w powrocie Foo spowodowałoby błąd kompilacji.

Czego mi tu brakuje?

+0

Niezwykle szokująca twoja specjalizacja nie jest wywoływana, jeśli wywołujesz 'Foo (a);' i oczekujesz, że specjalizacja 'Foo ()' zostanie wyrzucona. – WhozCraig

+0

@WhozCraig Zgadzam się, byłem zdesperowany, ponieważ to nie miało sensu. – JaredC

+0

Oczywiście uruchamia się, jeśli bezpośrednio wywołasz 'Foo ()', ale to nie jest to, czego ostatecznie szukasz, bez wątpienia. – WhozCraig

Odpowiedz

6

Przede wszystkim, to nic nie warte, że problem faktycznie ma związek z szablonów, ponieważ ten kod kompiluje dobrze:

typedef std::string& T; 
T Foo(int& i) { 
    return T(i); 
} 

Powodem myślę to kompiluje to, że oświadczenie return jest równoważna

return reinterpret_cast<T>(i); 

w przypadku T stanie się członkiem referencyjnym. ... i to, oczywiście, kompiluje: Obiecałeś, że wiesz, co robisz i poprosiłeś kompilatora, aby ci uwierzył.

OK, znalazłem go na 5.2.3 [expr.type.conv] ustęp 1:

... Jeśli lista ekspresja jest pojedynczy wyraz, wyrażenie typu konwersja jest równoważny (w definedness, i jeśli zdefiniowane w znaczeniu) do odpowiedniego wyrażenia rzutowania (5.4). ...

... i 5,4 [expr.cast] ustęp 4:

Konwersje wykonywane przez [inne formy odlewów] reinterpret_cast (5.2.10) [...] można wykonać za pomocą notacji rzutowej konwersji typu jawnego. [...]

(na elisions obejmować przypadki obejmujące typ zdefiniowany przez użytkownika, wbudowany konwersji typu, const konwersji, etc.)

+0

Czy możesz rozwinąć * dlaczego * 'reinterpret_cast' jest wywoływane w tym przypadku, gdy T jest odniesieniem? – JaredC

+0

Wow, to subtelne, zwłaszcza przy użyciu szablonów. Widzę pytanie uzupełniające o zakazie typu referencyjnego dla "T" w tym przypadku ... – JaredC

+0

zobacz koniec mojej odpowiedzi, jak to zabronić. –

5

To nie ma nic wspólnego z szablonów, można uzyskać ten sam rezultat jeśli T jest po prostu typedef dla std::string& raczej niż wyprowadzona parametru szablonu:

#include <string> 

typedef std::string& T; 

T Foo(int & i) 
{ 
    return T(i); 
} 

int main() 
{ 
    int a = 1; 
    std::string & s = Foo(a); 
} 

odpowiedź Dietmar za uświadomił mi, to może być jeszcze bardziej uproszczone do:

#include <string> 

typedef std::string& T; 

int main() 
{ 
    int a = 1; 
    std::string & s = T(a); 
} 

gdzie T(a) jest taka sama jak obsady (T)a tj (std::string&)a które (zgodnie z zasadami 5,4 [expr.cast]) zrobi const_cast jeśli to ważne (co nie jest) lub static_cast jeśli to ważne (co nie jest) lub static_cast następnie const_cast jeśli to ważne (co nie jest) lub reinterpret_cast jeśli to ważny (które jest) lub reinterpret_cast następnie const_cast jeśli to ważne, inaczej ekspresja jest źle sformułowana.

Więc jak Dietmar powiedział, to to samo, co robi reinterpret_cast, tj

std::string & s = reinterpret_cast<std::string&>(a); 

Uważam to dość zaskakujące, że oryginalny kod kompiluje, ale jak to jest taka sama jak tej linii powyżej, to wolno kompilować . Używanie wyniku rzutowania jest jednak niezdefiniowanym zachowaniem.

Aby uniknąć zaskoczenia, gdy T(a) jest odpowiednikiem rzutowania, użyj nowej, jednolitej składni inicjalizacji C++ 11, T{a}, która jest zawsze inicjalizacją, a nie wyrażeniem rzutowania.

Świetne pytanie, badanie i udzielenie odpowiedzi pokazało mi nowe poczucie, o którym wcześniej nie wiedziałem, dzięki JaredC i Dietmarowi za nową wiedzę!

+0

Czy należy edytować pytanie, aby zdalnie odesłać szablony (skoro 2 osoby skomentowały to do tej pory)? Czy powinienem po prostu wyjść tak, jak jest? – JaredC

+0

Wydaje mi się, że można go edytować ... pytanie jest interesujące, więc warto je wyjaśnić, aby usunąć czerwone śledzia dotyczące szablonów. –

+0

@JaredC: Zgadzam się z Jonathanem. Oczywiście oznaczałoby to również, że odpowiedzi są edytowane w celu uwzględnienia zmienionego pytania;) –

Powiązane problemy