首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++11:移动/复制构造多义性?

C++11:移动/复制构造多义性?
EN

Stack Overflow用户
提问于 2012-02-06 04:37:34
回答 4查看 10.4K关注 0票数 24

在C++11中,我们可以定义复制和移动构造函数,但是在同一个类上这两个构造函数是允许的吗?如果是这样,您如何消除它们的用法的歧义?例如:

代码语言:javascript
复制
Foo MoveAFoo() {
  Foo f;
  return f;
}

上面的是副本吗?一步棋?我怎么知道?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 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值:

  • 隐式或显式调用函数的结果,其返回类型是对对象类型的右值引用,
  • 转换为对对象类型的右值引用,
  • 是指定非引用类型的非静态数据成员的类成员访问表达式,其中对象表达式是x值,或
  • 是指向成员的指针表达式,其中第一个操作数是x值,第二个操作数是指向数据成员的指针。

< .* >F216

通常,此规则的效果是命名的右值引用被视为左值,对对象的未命名的右值引用被视为rvalue;对函数的右值引用被视为左值,无论是否命名。-end备注]

例如,如果可以执行NRVO,则如下所示:

代码语言:javascript
复制
void MoveAFoo(Foo* f) {
    new (f) Foo;
}

Foo myfoo; // pretend this isn't default constructed
MoveAFoo(&myfoo);

如果不能做NRVO,但是Foo是可移动的,那么您的示例有点像这样:

代码语言:javascript
复制
void MoveAFoo(Foo* fparam) {
    Foo f;

    new (fparam) Foo(std::move(f));
}

Foo f; // pretend this isn't being default constructed
MoveAFoo(&f);

如果它不能被移动但可以被复制,那么它是这样的

代码语言:javascript
复制
void MoveAFoo(Foo* fparam) {
    Foo f;

    new (fparam) Foo((Foo&)f);
}

Foo f; // pretend this isn't default constructed
MoveAFoo(&f);
票数 28
EN

Stack Overflow用户

发布于 2012-02-06 04:49:24

为了支持@Seth,下面是标准中的相关段落:

§12.8 [class.copy] p32

当满足或将满足用于省略复制操作的标准时,除非源对象是函数参数,并且要复制的对象由左值指定,否则首先执行重载解析以选择用于复制的构造函数,就好像对象是由右值指定的一样。如果重载解析失败,或者如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能是cv限定的),则会再次执行重载解析,并将对象视为左值。注意:无论是否会发生副本省略,都必须执行此两阶段重载解决方案。它确定在不执行省略的情况下要调用的构造函数,并且即使省略了调用,选定的构造函数也必须是可访问的。-end笔记

票数 8
EN

Stack Overflow用户

发布于 2012-02-06 04:49:02

“消歧”只是你的老朋友,重载解决方案:

代码语言:javascript
复制
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 --这是一个特殊的新规则,可以简化移动语义的使用。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9152798

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档