返回值优化和私有拷贝构造函数

时间:2012-06-26 14:15:19

标签: c++

我写了一个简单的链接列表,因为最近的一次访谈编程挑战向我展示了我的C ++有多么生疏。在我的列表中,我声明了一个私有拷贝构造函数,因为我想明确避免制作任何副本(当然还有懒惰)。当我想通过拥有我的一个列表的值返回一个对象时,我遇到了一些麻烦。

class Foo
{
   MyList<int> list;  // MyList has private copy constructor

   public:
      Foo() {};
};

class Bar
{
   public:
      Bar() {};

      Foo getFoo()
      {
         return Foo();
      }
};

当我尝试按值返回Foo对象时,我收到编译器错误,指出MyList有一个私有拷贝构造函数。 Return-Value-Optimization是否应该否定任何复制的需要?我需要写一个复制构造函数吗?在我开始寻找这个问题的解决方案之前,我从未听说过移动构造器,这是最好的解决方案吗?如果是这样,我将不得不阅读它们。如果没有,解决这个问题的首选方法是什么?

3 个答案:

答案 0 :(得分:5)

标准明确指出构造函数仍然需要可访问,即使它被优化掉了。请参阅最近草稿中的12.8/32

在这种情况下,我更喜欢使对象可移动且不可复制。它使所有权非常清晰明确。

否则,您的用户始终可以使用shared_ptr。隐藏共享所有权充其量只是一个值得怀疑的想法(除非您能保证所有值都是不可变的)。

答案 1 :(得分:3)

基本问题是按值返回可能复制。标准不要求C ++实现应用copy-elision。这就是为什么对象仍然必须是可复制的:所以实现决定何时使用它不会影响代码是否格式正确。

无论如何,它不一定适用于用户可能喜欢的每个副本。例如,没有复制任务的省略。

我认为你的选择是:

  • 实施正确的副本。如果某人由于复制它而导致程序缓慢,那么他们的探查者会告诉他们,如果你不想这样做,你就不必把它当作阻止他们的工作。
  • 实现正确的移动,但没有副本(仅限C ++ 11)。
  • 更改getFoo以获取Foo&(或可能是Foo*)参数,并通过某种方式改变其对象来避免副本。一个有效的swap会派上用场。如果getFoo确实返回了一个默认构造的Foo,那么这是毫无意义的,因为调用者需要在调用Foo之前构造getFoo
  • 返回包含在智能指针中的动态分配的Fooauto_ptrunique_ptr。定义为创建对象并将唯一所有权转移给其调用者的函数不应返回shared_ptr,因为它没有release()函数。
  • 提供一个复制构造函数,但如果它曾被使用过,它会以某种方式爆炸(无法链接,中止,抛出异常)。这样做的问题是(1)它注定要失败,但是编译器什么也没说,(2)你强制执行的质量,所以如果有人因为某种原因故意禁用了RVO,那么你的课程就无法工作。

我可能错过了一些。

答案 2 :(得分:2)

解决方案是实现您自己的复制构造函数,该构造函数将使用MyList的其他方法来实现复制语义。

  

......我想明确避免制作任何副本

你必须选择。要么你不能复制一个对象,比如std::istream;然后你必须在指针/引用中保存这些对象,因为这些可以被复制(在C ++ 11中,你可以使用移动语义)。或者你实现了复制构造函数,这可能更容易解决每个地方需要副本的问题。