如何实施复制省略,为什么它与删除的复制构造函数无法使用?

26

我有一个无法复制的类。复制这个类可能会出问题。我想要保证它永远不会被复制,因此我将其复制构造函数设为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 functionA::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 functionA::A(const A&)’
   A a = fun();
             ^
t.cc:4:5: note: declared here
     A(const A&) = delete;
     ^

但这是一个非常明显需要使用复制省略的情况,因此复制构造函数不应该被调用。为什么呢?


1
等到C++17吧,也许会有保证。 - Piotr Skotnicki
1
Jesper没有重复你的答案,你在我们的评论中没有提到即将发生的更改。 - Piotr Skotnicki
1
@PiotrSkotnicki 对不起,我没有看到你的评论。 - juanchopanza
在这种情况下,我看到的最常见解决方案是移动该类,如果不能移动,则返回std :: unique_ptr <A> - Chris Beck
3个回答

23

在C++17之前,复制省略是一种编译器不需要执行的优化,因此类必须可复制,因为编译器可能想要复制(即使它实际上并没有)。在C++17中,复制省略将在许多情况下得到保证,然后类就不需要复制构造函数了。

另请参阅:

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/ (关于"担保复制省略"的部分)

你可以使用一个老技巧,在你的类中声明复制构造函数但不实际实现它?只要它不实际调用复制构造函数,那么编译器应该会满意。我没有测试过,但我相信在C++17到来之前,这对你的情况应该有效。


2
是的 - 实际实现的复制构造函数抛出了致命异常。 - peterh

11

你目前无法强制执行复制省略(请参见其他答案)。

但是,您可以为您的类提供默认的移动构造函数,这将在无法使用RVO/NRVO时移动(而不是复制)返回值。要执行此操作,您应该为移动构造函数添加 = default

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

示例


1
@peterh 可能是因为它没有回答你所问的问题。 - juanchopanza
1
@juanchopanza 可能是可能的,但我不清楚为什么。在这个答案中,什么不清楚,为什么需要移动构造函数。 - peterh
2
@juanchopanza虽然缺乏解释,但它回答了问题。这是一种有效的方法,可以保证类永远不会被复制,同时仍允许OP的“return”语句正常工作。 - user743382
3
实际上,没有任何移动操作在进行,但从语义上讲,必须存在一个复制或移动的过程,因为C++返回的是值。在C++98中,这意味着需要始终使用复制构造函数。在C++11中,可以用移动构造函数替代它。在某些情况下,在C++17中两者都不需要。 - KABoissonneault
3
第三个问题是在“我想保证它永远不会被复制”之后隐含了一个“我该怎么做?”的问题 :) - user743382
显示剩余6条评论

7

返回值优化(RVO和NRVO)并不意味着可复制或可移动的类型被丢弃的要求。无论是否获得RVO,此要求都适用。

这样做最可能的原因是复制省略目前并未被强制执行。它是一种可能发生的优化,代码的编译与否不应基于该优化在特定实现中是否应用。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接