重载参考,而不是唯一的传值+ std :: move?

看来有关C ++ 0x的右值的主要build议是添加移动构造函数并将运算符移到您的类中,直到编译器默认实现它们。

但是如果你使用VC10,等待是一个失败的策略,因为在VC10 SP1之前,自动生成可能不会在这里,或者在最坏的情况下VC11。 可能的话,这个等待会以年计算。

这是我的问题。 编写所有这些重复的代码并不好玩。 而且看起来不愉快。 但这是一个很受欢迎的负担,因为这些课程被认为是缓慢的。 数百乃至数千个较小的class级并非如此。

::叹息:: C ++ 0x应该让我写更less的代码,而不是更多!

然后我有一个想法。 许多人分享,我猜想。

为什么不把价值通过一切? 不会std :: move + copy elision使这近乎最佳?

示例1 – 典型的Pre-0x构造函数

OurClass::OurClass(const SomeClass& obj) : obj(obj) {} SomeClass o; OurClass(o); // single copy OurClass(std::move(o)); // single copy OurClass(SomeClass()); // single copy 

缺点: rvalues浪费的副本。

示例2 – 推荐的C ++ 0x?

 OurClass::OurClass(const SomeClass& obj) : obj(obj) {} OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {} SomeClass o; OurClass(o); // single copy OurClass(std::move(o)); // zero copies, one move OurClass(SomeClass()); // zero copies, one move 

优点:推测是最快的。
缺点:很多代码!

示例3 – 按值传递+ std :: move

 OurClass::OurClass(SomeClass obj) : obj(std::move(obj)) {} SomeClass o; OurClass(o); // single copy, one move OurClass(std::move(o)); // zero copies, two moves OurClass(SomeClass()); // zero copies, one move 

优点:没有额外的代码。
缺点:情况1和2浪费。如果SomeClass没有移动构造函数,性能将受到很大的影响。


你怎么看? 它是否正确? 与代码减less的好处相比,是否发生了普遍可以接受的损失?

我对你的问题感兴趣,因为我对这个话题很陌生,做了一些研究。 让我来介绍一下结果。

首先,你的叹气。

::叹息:: C ++ 0x应该让我写更less的代码,而不是更多!

它也应该是给你一个更好的控制代码。 而且它确实如此。 我会坚持一个额外的构造函数:

 OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {} 

我个人喜欢在复杂和重要的情况下冗长,因为它让我和我的代码可能的读者提醒。

例如,C风格的cast (T*)pT和C ++标准static_cast<T*>(pT)更加冗长 – 但向前迈进了一大步。

其次,我对你的例3,最后一个testing案例有点怀疑。 我认为可能有另一个移动构造函数参数从右值创build传值的参数。 所以我在我的新VS2010中创build了一些快速项目,并得到了一些澄清。 我将在这里发布代码以及结果。

来源:

 // test.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <utility> #include <iostream> class SomeClass{ mutable int *pVal; public: int Val() const { return *pVal; }; SomeClass(int val){ pVal = new int(val); std::cout << "SomeClass constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl; } SomeClass(const SomeClass& r){ pVal = new int(r.Val()); std::cout << "SomeClass copy constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl; } SomeClass(const SomeClass&& r){ pVal = r.pVal; r.pVal = 0; std::cout << "SomeClass move constructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl; } ~SomeClass(){ if(pVal) delete pVal; std::cout << "SomeClass destructor(pVal = 0x" << std::hex << pVal << std::dec << ")" << std::endl; } }; class OtherClass{ SomeClass sc; public: OtherClass(int val):sc(val){ } 

注意这个部分:

 #if 1 OtherClass(SomeClass r):sc(std::move(r)){ } #else OtherClass(const SomeClass& r):sc(r){ } OtherClass(const SomeClass&& r):sc(std::move(r)){ } #endif 

  int Val(){ return sc.Val(); } ~OtherClass(){ } }; #define ECHO(expr) std::cout << std::endl << "line " << __LINE__ << ":\t" #expr ":" << std::endl; expr int _tmain(int argc, _TCHAR* argv[]) { volatile int __dummy = 0; ECHO(SomeClass o(10)); ECHO(OtherClass oo1(o)); __dummy += oo1.Val(); ECHO(OtherClass oo2(std::move(o))); __dummy += oo2.Val(); ECHO(OtherClass oo3(SomeClass(20))); __dummy += oo3.Val(); ECHO(std::cout << __dummy << std::endl); ECHO(return 0); } 

正如你所指出的那样,有一个编译时开关允许我testing这两种方法。

结果最好在文本比较模式下查看,在左边你可以看到#if 1编译,这意味着我们检查提议的解决方法,在右边 – #if 0 ,这意味着我们检查的“犹太教”方式c ++ 0x!

我错误地怀疑编译器做了些愚蠢的事情; 它在第三个testing用例中保存了额外的移动构造函数。

但说实话,在build议的解决方法中,我们必须考虑另外两个析构函数,但是这确实是一个小缺陷,考虑到如果在被破坏的对象上发生移动,则不应执行任何操作。 不过,这是很好的知道。

在任何情况下,我都不想离开,最好在包装类中另外编写一个构造函数。 这只是几行的问题,因为所有乏味的工作已经在SomeClass中完成, 必须有一个移动构造函数。