在C++ 11中,我们可以定义复制和移动构造函数,但是它们都允许在同一个类中?如果是这样,你如何消除他们的使用歧义?例如:
Foo MoveAFoo() {
Foo f;
return f;
}
Run Code Online (Sandbox Code Playgroud)
以上是副本吗?一个动作?我怎么知道?
Set*_*gie 28
通常它既不会归因于RVO.
如果无法执行该优化,那么它将是一个移动,因为返回的对象超出范围(并且将在之后被销毁).如果无法移动,则会复制它.如果无法复制,则无法编译.
移动构造函数的重点是,当一个副本将要被一个即将被销毁的对象组成时,通常不需要制作一个完整的副本,并且资源可以从垂死的对象移动到该对象正在被创造.
您可以根据要移动/复制的对象将要发生的事件来判断何时复制或移动构造函数.它是否会超出范围并被破坏?如果是这样,将调用移动构造函数.如果没有,复制构造函数.
当然,这意味着您可以在同一个类中同时拥有移动构造函数和复制构造函数.您还可以拥有复制赋值运算符和移动赋值运算符.
更新:可能不清楚何时调用移动构造函数/赋值运算符与普通复制构造函数/赋值运算符.如果我理解正确的话,如果使用xvalue(eXpiring值)初始化对象,则调用move构造函数.§3.10.1标准说
xvalue("eXpiring"值)也指对象,通常接近其生命周期的末尾(例如,可以移动其资源).xvalue是涉及rvalue引用的某些表达式的结果(8.3.2).[示例:调用返回类型为右值引用的函数的结果是xvalue. - 末端的例子]
标准§5的开头说:
[注意:表达式是xvalue,如果它是:
- 调用函数的结果,无论是隐式还是显式,其返回类型是对象类型的右值引用,
- 强制转换为对象类型的右值引用,
- 一个类成员访问表达式,指定非引用类型的非静态数据成员,其中对象表达式是xvalue,或者
- a.*指向成员的表达式,其中第一个操作数是xvalue,第二个操作数是指向数据成员的指针.
通常,此规则的作用是将命名的右值引用视为左值,将对象的未命名右值引用视为xvalues; 对函数的右值引用被视为左值,无论是否命名. - 尾注]
举个例子,如果可以完成NRVO,就像这样:
void MoveAFoo(Foo* f) {
new (f) Foo;
}
Foo myfoo; // pretend this isn't default constructed
MoveAFoo(&myfoo);
Run Code Online (Sandbox Code Playgroud)
如果NRVO无法完成但Foo可移动,那么您的示例有点像这样:
void MoveAFoo(Foo* fparam) {
Foo f;
new (fparam) Foo(std::move(f));
}
Foo f; // pretend this isn't being default constructed
MoveAFoo(&f);
Run Code Online (Sandbox Code Playgroud)
如果它不能移动但可以复制,那就像这样
void MoveAFoo(Foo* fparam) {
Foo f;
new (fparam) Foo((Foo&)f);
}
Foo f; // pretend this isn't default constructed
MoveAFoo(&f);
Run Code Online (Sandbox Code Playgroud)
要备份@Seth,这是标准中的相关段落:
§12.8 [class.copy] p32
当满足或将满足复制操作的省略标准时,除了源对象是函数参数这一事实,并且要复制的对象由左值指定,重载决策选择复制的构造函数是首先执行,好像对象是由右值指定的.如果重载决策失败,或者所选构造函数的第一个参数的类型不是对象类型的rvalue引用(可能是cv-qualified),则再次执行重载决策,将对象视为左值.[ 注意:无论是否发生复制省略,都必须执行此两阶段重载决策.如果未执行elision,它将确定要调用的构造函数,并且即使调用被省略,也必须可以访问所选的构造函数.- 尾注 ]