如何强制执行复制省略,为什么它不能与删除的复制构造函数一起使用?

pet*_*ica 20 c++ copy-elision c++11

我有一个无法上课的课程.复制这将是有问题的.我想保证它不会被复制,所以我制作了它的复制构造函数deleted:

class A {
  public:
    A();
    A(const A&) = delete;
};

A fun() {
  return A();
};

int main() {
  A a = fun();
};
Run Code Online (Sandbox Code Playgroud)

不幸的是,g ++不能编译这个原因:

t.cc: In function ‘A fun()’:
t.cc:8:12: error: use of deleted function ‘A::A(const A&)’
   return A();
            ^
t.cc:4:5: note: declared here
     A(const A&) = delete;
     ^
t.cc: In function ‘int main()’:
t.cc:12:13: error: use of deleted function ‘A::A(const A&)’
   A a = fun();
             ^
t.cc:4:5: note: declared here
     A(const A&) = delete;
     ^
Run Code Online (Sandbox Code Playgroud)

但是这是一个非常明确的情况,应该使用复制省略,因此不应该调用复制构造函数.为什么会这样?

Jes*_*uhl 21

直到C++ 17复制省略是一种优化,编译器不需要这样做,因此类必须是可复制的,因为编译器可能想要复制(即使它实际上没有).在C++ 17中,许多情况下将保证复制省略,然后类不需要复制ctors.

也可以看看:

http://en.cppreference.com/w/cpp/language/copy_elision

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0.html

https://herbsutter.com/2016/06/30/trip-report-summer-iso-c-standards-meeting-oulu/ (关于"保证副本省略"的内容)

你也许可以使用在你的类中声明复制构造函数的旧技巧,但实际上并没有实现它?只要它不实际调用复制ctor,这应该取悦编译器.我没有测试过,但我相信它应该适用于你的情况,直到C++ 17到来.

  • 是的 - 实际实现的复制构造函数抛出了一个致命的异常。 (2认同)

Som*_*ken 11

你不能强制复制elision(还)(见其他答案).

但是,您可以为类提供默认的移动构造函数,如果无法使用RVO/NRVO,则会移动(因此,不复制)返回值.要执行此操作,您应该= default为移动构造函数添加:

class A {
  public:
    A() = default;
    A(const A&) = delete;
    A(A&&) = default;
    A& operator=(A&&) = default;
};
Run Code Online (Sandbox Code Playgroud)

  • @peterh实际上,没有动作,但在语义上必须有一个副本或一个移动,因为C++按值返回.在C++ 98中,这意味着总是需要复制构造函数.在C++ 11中,您可以将其替换为移动构造函数.在C++ 17中,在某些情况下,两者都不需要 (3认同)
  • @juanchopanza第三个问题是隐含的"我该怎么做?" 在"我想保证它不会被复制":) (3认同)
  • @juanchopanza它正在回答这个问题,尽管由于缺乏解释而很差.这是一种保证类永远不会被复制的有效方法,同时仍允许OP的`return`语句工作. (2认同)

jua*_*nza 7

返回值优化(RVO和NRVO)并不意味着要求可复制或可移动所涉及的类型被删除.无论您是否获得RVO,此要求均适用.

最可能的原因是(目前)不执行复制省略.这是一种可能发生的优化,根据是否在特定实现中应用该优化,编译或不编译代码是没有意义的.

在C++ 17中,RVO将在某些情况下强制执行,并且可复制性和可移动性的要求将被删除.