似乎有关 C++0x 右值的主要建议是将 move 构造函数和 move 运算符添加到您的类中,直到编译器默认实现它们。
但是,如果您使用 VC10,等待是一种失败的策略,因为自动生成可能要到 VC10 SP1,或者在最坏的情况下,VC11 才会出现。很可能,等待的时间会以年为单位。
这就是我的问题。编写所有这些重复的代码并不好玩。而且看着很不爽。但对于那些被认为速度较慢的类(class)来说,这是一个很受欢迎的负担。对于数百甚至数千个较小的类(class)来说,情况并非如此。
::sighs::C++0x 应该让我写less 代码,而不是更多!
然后我有了一个想法。我猜是很多人分享的。
为什么不按值传递所有内容? std::move + 复制省略不会使这几乎是最优的吗?
示例 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
缺点:浪费了右值的拷贝。
示例 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
没有 move 构造函数,性能将受到很大影响。
你怎么看?这个对吗?与代码减少的好处相比,所发生的 move 是否是普遍可以接受的损失?
最佳答案
我对您的问题很感兴趣,因为我是该主题的新手并且做了一些研究。 让我介绍一下结果。
首先,你的叹息。
::sighs:: C++0x was supposed to let me write less code, not more!
它还应该让您更好地控制代码。它确实如此。 我会坚持使用额外的构造函数:
OurClass::OurClass(SomeClass&& obj) : obj(std::move(obj)) {}
我个人更喜欢在复杂和重要的情况下使用冗长,因为它让我和我的代码的潜在读者保持警惕。
以 C 风格的类型转换 (T*)pT
为例和 C++ 标准 static_cast<T*>(pT)
更加冗长 - 但向前迈出了一大步。
其次,我对您的示例 3(最后一个测试用例)有点怀疑。我认为可能涉及另一个 move 构造函数来从右值创建按值传递的参数。所以我在我的新 VS2010 中创建了一些快速项目并得到了一些澄清。我将在这里发布代码以及结果。
来源:
// 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);
}
正如您所指出的,有一个编译时开关可以让我测试这两种方法。
The results最好在文本比较模式下查看,在左侧您可以看到 #if 1
编译,这意味着我们检查建议的解决方法,在右侧 - #if 0
,意味着我们检查c++0x中描述的“kosher”方式!
我错了,我怀疑编译器做蠢事;它在第三个测试用例中保存了额外的 move 构造函数。
但老实说,我们必须考虑在建议的解决方法中调用了另外两个析构函数,但这肯定是一个小缺点,考虑到如果在被破坏的对象上发生 move ,则不应执行任何操作.不过,很高兴知道。
在任何情况下,我都不想在包装类中编写另一个构造函数更好。这只是几行的问题,因为所有繁琐的工作都已经在 SomeClass
中完成了。 必须有一个 move 构造函数。
关于c++ - 引用重载,而不是唯一的按值传递+ std::move?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2839127/