这是一个奇怪的地方,我不知道,如果是C ++标准,我的编译器(Ubuntu 12.04上的G ++版本4.6.3,这是Ubuntu的最新长期支持版本)或者我,谁不明白; - )
有问题的代码简单如下:
#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 ++的一个错误,也许是我正在使用的G ++的特定版本?
更令人费解的是,尽管MyListClass也是私有的(但是扩展了一个“官方”类,其中可能存在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!
}
但只是从对象更改为指针,编译再次失败:
#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!
}
当然,在我的实际应用中,课程更复杂;我尽可能地简化了代码,以便仍能重现问题。
答案 0 :(得分: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
添加了任何新成员变量,那么不将被交换。
所有这些,并且只是作为旁注:你不应该继承标准容器,因为它们从未被设计为继承。
[*]免责声明:我不知道该特定版本的编译器是否支持此功能,您必须仔细检查。