简体   繁体   English

std ::用G ++交换怪异

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

This is a weirdness, where I don't know, if it is with the C++ standard, with my compiler (G++ version 4.6.3 on Ubuntu 12.04, which is the latest long-term support version of Ubuntu) or with me, who doesn't understand ;-) 这是一个奇怪的,我不知道,如果它是C ++标准,我的编译器(Ubuntu 12.04上的G ++版本4.6.3,这是Ubuntu的最新长期支持版本)或我,谁不明白;-)

The code in question is as simple as follows: 有问题的代码简单如下:

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

When trying to compile with G++, the compiler yields the following error message: 尝试使用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])

The surprising result is, that just moving the class definition out of the function makes that code compile fine: 令人惊讶的结果是,只是将类定义移出函数使得代码编译正常:

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

So is it, that std::swap() is not supposed to work on classes, which are private to functions? 那么,std :: swap()不应该在类上工作,这些类是函数私有的吗? Or is this a bug with G++, maybe the specific version of G++ I am using? 或者这是G ++的一个错误,也许是我正在使用的G ++的特定版本?

Even more puzzling is, that the following does work again, despite MyListClass is also private (but extends an "official" class, for which maybe a specific implementation of swap() exists): 更令人费解的是,尽管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!
}

But just change from objects to pointers, and compiling fails again: 但只是从对象更改为指针,再次编译失败:

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

Of course, in my real application the classes are more complex; 当然,在我的实际应用中,类更复杂; I simplified the code as much as possible to still reproduce the problem. 我尽可能地简化代码以仍然重现问题。

If you are running in C++03 mode, which I believe to be the case, you are not allowed to use a locally defined type in a template. 如果您在C ++ 03模式下运行(我认为是这种情况),则不允许在模板中使用本地定义的类型。 If this is the case you can define your types at the namespace level to make it work, or else you can compile in C++11 mode where it should compile.[*] 如果是这种情况,您可以在命名空间级别定义类型以使其工作,否则您可以在C ++ 11模式下编译它应该编译。[*]

In case you wonder why the second case works, the standard does not provide specializations of 如果你想知道为什么第二种情况有效,标准不提供专业化

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

as std::list is a template itself and you cannot partially specialize template functions. 因为std::list本身就是一个模板,你不能部分专门化模板功能。 What it provides is a different base template: 它提供的是一个不同的基本模板:

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

Now as in the previous case, the compiler cannot use your local type with [1], so that is discarded. 现在和前面的情况一样,编译器不能将本地类型与[1]一起使用,因此会被丢弃。 Then it tries [2], and it finds that it can convert the lvalue of the local type to references to the base std::list<int> , and after that conversion [2] is a good candidate. 然后它尝试[2],它发现它可以将本地类型的左值转换为对基本std::list<int>引用,之后转换[2]是一个很好的候选者。 It will then call 然后它会打电话

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

which does not use a local type, but rather the namespace level std::list<int> . 它不使用本地类型,而是使用命名空间级别std::list<int>

On the other hand, the fact that it compiles does not mean that it does what you want. 另一方面,它编译的事实并不意味着它做你想要的。 In particular, if the extended type MyListClass adds any new member variables, those will not be swapped. 特别是,如果扩展类型MyListClass添加了任何新的成员变量,那么这些变量将不会被交换。

All that being said, and just as a side note: you should not inherit from standard containers, as they were never designed to be inherited from. 所有这些,只是作为旁注:你不应该继承标准容器,因为它们从来没有被设计为继承。

[*] Disclaimer: I don't know whether this feature is supported in that particular version of the compiler, you will have to double check. [*]免责声明:我不知道在特定版本的编译器中是否支持此功能,您必须仔细检查。

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

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