2013-06-27 12 views
10

To jest dziwactwo, gdzie nie wiem, jeśli jest ze standardem C++, z moim kompilatorem (G ++ wersja 4.6.3 na Ubuntu 12.04, który jest najnowszym długoterminowym wsparcie wersja Ubuntu) lub ze mną, kto nie rozumie ;-)std :: zamień dziwność z G ++

Kod w pytaniu jest tak proste, jak następuje:

#include <algorithm> // for std::swap 
void f(void) 
{ 
    class MyClass { }; 
    MyClass aa, bb; 
    std::swap(aa, bb);   // doesn't compile 
} 

Kiedy próbuje skompilować z g ++, kompilator daje następujące komunikat o błędzie:

test.cpp: In function ‘void f()’: 
test.cpp:6:21: error: no matching function for call to ‘swap(f()::MyClass&, f()::MyClass&)’ 
test.cpp:6:21: note: candidates are: 
/usr/include/c++/4.6/bits/move.h:122:5: note: template<class _Tp> void std::swap(_Tp&, _Tp&) 
/usr/include/c++/4.6/bits/move.h:136:5: note: template<class _Tp, long unsigned int _Nm> void std::swap(_Tp (&)[_Nm], _Tp (&)[_Nm]) 

Zaskakujący wynik jest, że po prostu się definicję klasy z funkcji sprawia, że ​​kod skompilować grzywny:

#include <algorithm> // for std::swap 
class MyClass { }; 
void f(void) 
{ 
    MyClass aa, bb; 
    std::swap(aa, bb);   // compiles fine! 
} 

Więc jest to, że std :: swap() nie ma do pracy na zajęciach, które są prywatny do funkcji? Czy jest to błąd w G ++, może konkretnej wersji G ++ używam?

Jeszcze bardziej zastanawiające jest, że następujący działa ponownie, mimo MyListClass jest także prywatny (ale rozciąga się „oficjalnego” klasę, do której być może istnieje konkretna realizacja transakcji swap()):

#include <algorithm>  // for std::swap 
#include <list>    // for std::list 
void g(void) 
{ 
    class MyListClass : public std::list<int> { }; 
    MyListClass aa, bb; 
    std::swap(aa, bb);    // compiles fine! 
} 

Ale po prostu przełącz się z obiektów na wskaźniki, a kompilacja ponownie się zawiesza:

#include <algorithm>  // for std::swap 
#include <list>    // for std::list 
void g(void) 
{ 
    class MyListClass : public std::list<int> { }; 
    MyListClass aa, bb; 
    MyListClass* aap = &aa; 
    MyListClass* bbp = &bb; 
    std::swap(aap, bbp); // doesn't compile! 
} 

Oczywiście w moim prawdziwym zastosowaniu klasy są bardziej złożone; Uprośniłem kod tak bardzo, jak to możliwe, aby nadal odtwarzać problem.

Odpowiedz

16

Jeśli pracujesz w trybie C++ 03, co moim zdaniem ma miejsce, nie możesz używać typu zdefiniowanego lokalnie w szablonie. W takim przypadku możesz zdefiniować swoje typy na poziomie przestrzeni nazw, aby działało, lub możesz skompilować w trybie C++ 11, w którym ma się skompilować. [*]

Jeśli zastanawiasz się, dlaczego drugi przypadek prace, standard nie przewiduje specjalizacje

template <typename T> void swap(T&,T&) // [1] 

jako std::list jest sam szablon i nie można częściowo specjalizują funkcje szablonów. Co to zapewnia to inny szablon bazowy:

template <typename T, typename A> void swap(list<T,A>&,list<T,A>&); // [2] 

Teraz jak w poprzednim przypadku, kompilator nie może korzystać z lokalnego typu [1], tak że jest odrzucany. Następnie próbuje [2], i okazuje się, że może przekonwertować lwartość lokalnego typu na referencje do bazy std::list<int>, a po tej konwersji [2] jest dobrym kandydatem. Będzie on następnie wywołać

std::swap(static_cast<std::list<int&>>(aa),static_cast<std::list<int&>>(bb)); 

które nie używa typu lokalnego, ale raczej poziom nazw std::list<int>.

Z drugiej strony fakt, że się kompiluje, nie oznacza, że ​​robi to, co chcesz. W szczególności, jeśli rozszerzony typ MyListClass dodaje dowolne nowe zmienne składowe, zostaną one zamienione na , a nie.

Wszystko to, co zostało powiedziane, i jak na marginesie: nie powinieneś dziedziczyć ze standardowych pojemników, ponieważ nie zostały one zaprojektowane do odziedziczenia.

[*] Zastrzeżenie: Nie wiem, czy ta funkcja jest obsługiwana w danej wersji kompilatora, trzeba będzie dwukrotnie sprawdzić.

+1

Według [wiki Apache] (http://wiki.apache.org/stdcxx/C%2B%2B0xCompilerSupport), GCC obsługuje to od wersji 4.5, więc powinno wystarczyć dodanie opcji '-std = c + + 0x'. – Angew

+0

Dziękuję bardzo za odpowiedzi! Dodanie opcji -std = C++ 0x sprawia, że ​​kompilacja jest dobra nawet w przypadku klas lokalnych. Wow, nie wiedziałem o niekompatybilności z lokalnymi klasami i szablonami w starszych standardach C++. –