按值返回会调用复制构造函数吗?

时间:2014-03-14 17:58:04

标签: c++ copy-constructor

template<class T>
T Stack<T>::pop()
{
   if (vused_ == 0)
   {
      throw "Popping empty stack";
   }
   else
   {
      T result = v_[used_ - 1];
      --vused_;
      return result;
   }
}

我不明白所有这些,或者说我不理解它,但据说这段代码不起作用,因为它按值返回,我猜它是指结果,并且调用复制构造函数,我不知道这是怎么回事。有人可以解释吗?

3 个答案:

答案 0 :(得分:3)

与问题示例中的代码不同,std::stack<T>::pop不会返回值。

那是因为如果需要复制项目类型并且复制抛出,那么您的操作失败会改变对象的状态,而无法重新建立原始状态。

即。返回值 - pop不提供 strong exception guarantee (成功或无变化)。

同样,抛出一个文字字符串至少可以说是非常规的。

因此,虽然代码本身没有任何错误(模块可能会输入错误,例如vused_v_等),但它在保证方面很弱,而且非常规可能会导致错误在其他地方的例外处理。


另一种观点认为pop的非值返回std::stack 不切实际,导致不必要的详细客户端代码。

对于使用堆栈对象,我更喜欢返回值pop

但它不是/或:可以根据popped(状态更改)和pop轻松定义值返回便捷方法 top (检查)。这种便捷方法具有较弱的异常保证。但客户端代码程序员可以选择。 : - )


现有设计的改进是支持可移动对象,即替换

<德尔> return result;

<德尔>与

<德尔> return move( result );

帮助编译器。

↑更正:
实际上,上面删除的文本具有与预期文本相反的效果,即它禁止RVO(保证构造函数调用)。不知怎的,我的想法在这里颠倒了。但作为一项规则,不要在move表达式上使用return,因为默认值是优化,添加的move可以没有改进的东西,但可以抑制RVO优化。

答案 1 :(得分:2)

是的,按值返回正式调用复制构造函数。但这根本不是问题,因为在实践中,编译器通常能够优化掉额外的副本。这种技术被称为&#34;返回值优化&#34;。

答案 2 :(得分:1)

除了return语句(如果类是可移动但不可复制时可以工作,例如你可以返回std::unique_ptr),问题就是你在这里做的副本:< / p>

 T result = v_[used_ - 1];

要使此副本成为可能,类型T必须是可复制的(例如T应该具有公共拷贝构造函数 - 上述语句所需 - 并且复制赋值operator=)。


作为旁注,抛出一个字符串非常糟糕:你应该抛出一个异常类,例如

throw std::runtime_error("Popping empty stack.");

或者只为这种情况定义一个 ad hoc 类并抛出它,例如:

class StackUnderflowException : public std::runtime_error
{
public:
     StackUnderflowException()
         : std::runtime_error("Popping empty stack.")
     { }
};

....
  throw StackUnderflowException();