C++11:移动/复制构造不明确
C++11: Move/Copy construction ambiguity?
在C++11中,我们可以定义复制和移动构造函数,但在同一个类上允许两者?如果是,你如何消除它们的用法歧义?例如:
Foo MoveAFoo() {
Foo f;
return f;
}
以上是复印件吗?搬家?我怎么知道?
通常都不是由于RVO。
如果不能进行优化,那么这将是一次移动,因为返回的对象超出了范围(并将在之后被销毁)。如果无法移动,则会进行复制。如果不能复制,它就不会编译。
move构造函数的全部意义在于,当要对即将销毁的对象进行复制时,通常不需要进行完整的复制,并且可以将资源从垂死的对象移动到正在创建的对象。
您可以根据要移动/复制的对象将要发生的情况来判断何时调用复制或移动构造函数。它是否即将超出范围并被销毁?如果是,将调用move构造函数。如果不是,则复制构造函数。
当然,这意味着您可能在同一个类中同时拥有move构造函数和copy构造函数。您还可以使用复制分配运算符和移动分配运算符。
更新:可能不清楚移动构造函数/赋值运算符与纯复制构造函数/赋值操作符的确切调用时间。如果我理解正确的话,如果一个对象是用xvalue(eXpiring值)初始化的,就会调用move构造函数。标准§3.10.1规定
xvalue("eXpiring"值)也指对象,通常在其生命周期的结束(以便其资源可以移动,因为示例)。xvalue是某些类型的表达式的结果涉及右值引用(8.3.2)。[示例:调用的结果返回类型为右值引用的函数是xvalue--终止示例]
标准§5的开头写道:
[注意:如果表达式是:,则它是一个xvalue
- 调用的结果函数,无论是隐式还是显式,其返回类型为对对象类型的右值引用
- 对的右值转换引用对象类型
- 类成员访问表达式,指定对象所在的非引用类型的非静态数据成员表达式是x值,或者
- 指向中成员表达式的.*指针其中第一操作数是x值,第二操作数是指向数据成员的指针
一般来说,这条规则的效果是命名右值引用被视为左值和未命名右值对对象的引用被视为xvalue;对的右值引用无论是否命名,函数都被视为左值--尾注]
举个例子,如果NRVO可以实现,它是这样的:
void MoveAFoo(Foo* f) {
new (f) Foo;
}
Foo myfoo; // pretend this isn't default constructed
MoveAFoo(&myfoo);
如果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);
如果它不能移动,但可以复制,那么它就像这个
void MoveAFoo(Foo* fparam) {
Foo f;
new (fparam) Foo((Foo&)f);
}
Foo f; // pretend this isn't default constructed
MoveAFoo(&f);
要备份@Seth,以下是标准中的相关段落:
§12.8 [class.copy] p32
当满足或将满足省略复制操作的标准时,除非源对象是函数参数并且要复制的对象由左值指定,否则首先执行选择复制构造函数的重载解析,就好像对象由右值指定一样。如果重载解析失败,或者所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是cv限定的),则会再次执行重载解析,将对象视为左值。[注意:无论是否会发生复制省略,都必须执行此两阶段重载解析。如果不执行省略,它将确定要调用的构造函数,并且即使调用被省略,所选构造函数也必须是可访问的。--end Note]
"消除歧义"只是你的老朋友,过载解决方案:
Foo y;
Foo x(y); // copy
Foo x(std::move(y)); // move
第一个例子中的表达式y
是类型为Foo
的左值,它绑定到Foo const &
(如果有这样的构造函数,也绑定到Foo &
);第二个例子中表达式std::move(y)
的类型是Foo &&
,因此它将与Foo &&
结合(如果没有前者,也与Foo const &
结合)。
在您的示例中,MoveAFoo()
的结果是一个类型为Foo
的临时构造函数,因此如果有Foo &&
-构造函数可用,它将绑定到该构造函数,否则将绑定到const-copy构造函数。
最后,在返回Foo
(按值)的函数中,如果x
是Foo
类型的局部变量,则语句return x;
等效于return std::move(x);
——这是一个特殊的新规则,可以更容易地使用移动语义。
Foo MoveAFoo() {
Foo f;
return f;
}
这是函数MoveAFoo
的定义,它返回类型为Foo
的对象。在其主体中,当局部Foo f;
超出其范围时创建并销毁。
在此代码中:
Foo x = MoveAFoo();
对象Foo f;
是在MoveAFoo
函数内部创建的,并直接分配到x
中,这意味着不调用复制构造函数。
但在这个代码中:
Foo x;
x = MoveAFoo();
在MoveAFoo
函数内部创建对象Foo f;
,然后创建f
的副本并将其存储到x
中,并销毁原始f
。
- Visual C++(VS2017)中用户定义的转换不明确
- 重载类方法的不明确调用
- 为函数定义符号不明确的指针参数
- 父类的私有函数会导致对具有相同名称和相似参数的子类中的公共函数的不明确调用
- 在 C++17 中的命名空间和子命名空间中重载运算符是不明确的
- C++ 编译器错误:P1LinkedList.cpp:145:错误:重载的"to_string(int&)"调用不明确
- 对重载函数find_first_not_of的不明确调用
- 不明确的成员模板查找
- gcc出现不明确的模板实例化错误
- 调用'Node'构造函数是不明确的
- 如何解决不明确的运算符过载问题?
- 通过基类接受方法转发派生 UniquePtr 的右值会移动引用而不是复制
- 除了 std::vector 之外,是否有一个 std 容器不会复制和销毁作为类的元素?
- 使用 nullptr 调用重载方法是不明确的
- "fpclassify":对重载函数的不明确调用
- 对"列表"的引用不明确,包括头文件
- 删除全局隐式函数 - 避免使用不明确的运算符
- 调用值构造函数而不是复制构造函数
- 错误:使用复制和交换习惯用法的交换函数中"operator="的重载不明确
- C++11:移动/复制构造不明确