在C++11中,我们可以定义复制和移动构造函数,但是在同一个类上这两个构造函数是允许的吗?如果是这样,您如何消除它们的用法的歧义?例如:
Foo MoveAFoo() {
Foo f;
return f;
}上面的是副本吗?一步棋?我怎么知道?
发布于 2012-02-06 04:39:38
通常,这都不是由于RVO造成的。
如果不能执行优化,那么它将是一个移动,因为返回的对象将超出作用域(并且将在之后被销毁)。如果它不能被移动,那么它将被复制。如果它不能被复制,它将无法编译。
移动构造函数的全部意义在于,当要对即将销毁的对象进行复制时,通常没有必要创建整个副本,并且可以将资源从垂死的对象移动到正在创建的对象。
您可以根据被移动/复制的对象将要发生的事情来判断何时调用复制或移动构造函数。它是否即将超出范围并被销毁?如果是这样,将调用move构造函数。如果不是,则返回复制构造函数。
当然,这意味着您可以在同一个类中同时拥有移动构造函数和复制构造函数。还可以使用复制赋值运算符和移动赋值运算符。
更新:可能不清楚移动构造函数/赋值运算符与普通复制构造函数/赋值运算符的确切调用时间。如果我理解正确的话,如果一个对象是用x值(eXpiring值)初始化的,那么就会调用移动构造函数。§3.10.1标准中规定
一个x值(一个“eXpiring”值)也指的是一个对象,通常在其生命周期接近尾声时(例如,这样它的资源就可以被移动)。xvalue是涉及rvalue引用(8.3.2)的特定类型表达式的结果。示例:调用返回类型为rvalue引用的函数的结果是xvalue。-end示例
该标准的第5款的开头写道:
[注意:如果表达式符合以下条件,则它是x值:
< .* >F216
通常,此规则的效果是命名的右值引用被视为左值,对对象的未命名的右值引用被视为rvalue;对函数的右值引用被视为左值,无论是否命名。-end备注]
例如,如果可以执行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);发布于 2012-02-06 04:49:24
为了支持@Seth,下面是标准中的相关段落:
§12.8 [class.copy] p32
当满足或将满足用于省略复制操作的标准时,除非源对象是函数参数,并且要复制的对象由左值指定,否则首先执行重载解析以选择用于复制的构造函数,就好像对象是由右值指定的一样。如果重载解析失败,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是cv限定的),则会再次执行重载解析,并将对象视为左值。注意:无论是否会发生副本省略,都必须执行此两阶段重载解决方案。它确定在不执行省略的情况下要调用的构造函数,并且即使省略了调用,选定的构造函数也必须是可访问的。-end笔记
发布于 2012-02-06 04:49:02
“消歧”只是你的老朋友,重载解决方案:
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 &&-constructor (如果有),否则绑定到const-copy构造函数。
最后,在返回Foo (按值)的函数中,如果return std::move(x);是Foo类型的局部变量,则语句x等同于Foo --这是一个特殊的新规则,可以简化移动语义的使用。
https://stackoverflow.com/questions/9152798
复制相似问题