繁体   English   中英

std ::用G ++交换怪异

[英]std::swap weirdness with G++

这是一个奇怪的,我不知道,如果它是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!
}

当然,在我的实际应用中,类更复杂; 我尽可能地简化代码以仍然重现问题。

如果您在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添加了任何新的成员变量,那么这些变量将不会被交换。

所有这些,只是作为旁注:你不应该继承标准容器,因为它们从来没有被设计为继承。

[*]免责声明:我不知道在特定版本的编译器中是否支持此功能,您必须仔细检查。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM