2013-06-27 72 views
10

這是一個奇怪的地方,我不知道它是否符合C++標準,使用我的編譯器(Ubuntu 12.04上的G ++版本4.6.3,這是最新的長期或Ubuntu的支持版本)與我,誰不明白;-)std ::與G ++交換奇怪

有問題的代碼很簡單如下:

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

當試圖用G ++編譯,編譯器產生如下錯誤消息:

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]) 

令人驚訝的結果是,這只是移動類定義了功能使得代碼編譯罰款:

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

所以是吧,那化std :: swap()不應該在類,這是工作私人功能?或者這是G ++的一個bug,也許是我使用的特定版本的G ++?

更令人費解的是,下列不重新工作,儘管MyListClass也是私有的(但延伸的「官方」類,對於這也許交換()的具體實施存在):

#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! 
} 

但是,僅僅從對象的指針發生變化,編譯再次失敗:

#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! 
} 

當然,在我的實際應用程序的類更復雜;我儘可能簡化了代碼,以再現問題。

回答

16

如果您在運行C++ 03模式,我認爲是這樣,你是不是在允許使用的模板本地定義的類型。如果是這種情況,您可以在名稱空間級別定義類型以使其工作,否則您可以在C++ 11模式下編譯它應編譯的內容。[*]

如果您想知道爲什麼第二種情況工程,標準不提供

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

std::list專業化是一個模板本身,你不能部分專業模板功能。它所提供的是一個不同的基礎模板:

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

現在,作爲前面的情況,編譯器無法使用本地類型與[1],因此被丟棄。然後它嘗試[2],它發現它可以將本地類型的左值轉換爲對基址std::list<int>的引用,並且在轉換之後[2]是一個很好的候選。然後,它會調用

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

不使用局部類型,而是命名空間層次std::list<int>

在另一方面,它編譯的事實並不意味着它你想要做什麼。特別是,如果擴展類型MyListClass添加了新的成員變量,那麼將會交換而不是

之所以這麼說,只是作爲一個側面說明:你不應該從標準集裝箱繼承,因爲它們從來沒有設計從繼承。

[*]免責聲明:我不知道該特性是否在該特定版本的編譯器中受支持,您必須仔細檢查。

+1

根據[Apache wiki](http://wiki.apache.org/stdcxx/C%2B%2B0xCompilerSupport),GCC自4.5開始支持這個功能,所以它應該足以添加選項'-std = c + + 0x'。 – Angew

+0

非常感謝您的回答!即使使用本地類,添加-std = C++ 0x也可以使事情順利進行。哇,我不知道在舊的C++標準中與本地類和模板的不兼容性。 –