24

我有一个不可复制的课程。复制这将是有问题的。我想保证它永远不会被复制,所以我创建了它的复制构造函数deleted

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

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

int main() {
  A a = fun();
};

不幸的是,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;
     ^

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

4

3 回答 3

22

直到 C++17 复制省略是编译器不需要做的优化,所以类必须是可复制的,因为编译器可能想要复制(即使它实际上没有复制)。在 C++17 中,在许多情况下都会保证复制省略,然后类就不需要复制 ctor。

也可以看看:

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 到来。

于 2016-07-06T13:18:22.690 回答
11

您不能强制复制省略(还)(请参阅其他答案)。

但是,您可以为您的类提供一个默认的移动构造函数,如果 RVO/NRVO 不可行,这将移动(因此,不复制)返回值。为此,您应该= default为移动构造函数添加:

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

例子

于 2016-07-06T13:10:30.613 回答
7

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

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

在 C++17 中,在某些情况下会强制执行 RVO,并且会放弃对可复制性和可移动性的要求。

于 2016-07-06T13:10:48.600 回答