【问题标题】:Return Value Optimization and private copy constructors返回值优化和私有复制构造函数
【发布时间】:2012-06-26 14:15:18
【问题描述】:

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

【问题讨论】:

  • RVO 是一种优化。如果您不允许复制构造,那么您将无法复制构造。如果您确实允许,则可以应用 RVO。
  • 优化不应该改变行为,相反,编译器只有在能够保证预期行为的情况下才能进行优化。要通过复制返回,对象必须是可复制的。
  • @lvella:虽然在这种情况下,优化允许改变行为 - 即使副本有副作用,也可以省略。但是你是正确的,复制构造函数必须是可访问的。
  • @lvella RVO 被允许改变行为。但是没有 RVO 的代码必须是合法的,所以编译器仍然期望找到一个有效的复制构造函数。

标签: c++


【解决方案1】:

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

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

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

【讨论】:

  • 那么我最好的做法是什么?为了编译器的缘故允许浅拷贝并希望没有人真正创建一个?
  • @jlunavtgrad 我为此类设计决策加了 2 美分。
【解决方案2】:

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

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

我认为您的选择是:

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

我可能错过了一些。

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-12
      相关资源
      最近更新 更多