1

我写了一个简单的链表,因为最近的一次面试编程挑战向我展示了我的 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 是否应该否定任何复制的需要?我需要编写一个复制构造函数吗?在我开始寻找这个问题的解决方案之前,我从来没有听说过移动构造函数,这是最好的解决方案吗?如果是这样,我将不得不阅读它们。如果不是,解决此问题的首选方法是什么?

4

3 回答 3

5

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

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

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

于 2012-06-26T14:16:59.137 回答
3

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

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

我认为你的选择是:

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

我可能错过了一些。

于 2012-06-26T15:17:07.090 回答
2

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

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

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

于 2012-06-26T14:22:57.397 回答