18

我从 gcc 收到一个奇怪的错误,不知道为什么。我制作了以下示例代码以使问题更加清晰。基本上,定义了一个类,我将其复制构造函数和复制赋值运算符设为私有,以防止意外调用它们。

#include <vector>
#include <cstdio>
using std::vector;

class branch 
{
public:
  int th;

private:
  branch( const branch& other );
  const branch& operator=( const branch& other );

public:

  branch() : th(0) {}

  branch( branch&& other )
  {
    printf( "called! other.th=%d\n", other.th );
  }

  const branch& operator=( branch&& other )
  {
    printf( "called! other.th=%d\n", other.th );
    return (*this);
  }

};



int main()
{
  vector<branch> v;
  branch a;
  v.push_back( std::move(a) );

  return 0;
}

我希望这段代码能够编译,但是 gcc 失败了。实际上 gcc 抱怨“branch::branch(const branch&) 是私有的”,据我所知不应该被调用。

赋值运算符有效,因为如果我将 main() 的主体替换为

branch a;
branch b;
b = a;

它将按预期编译和运行。

这是 gcc 的正确行为吗?如果是这样,上面的代码有什么问题?任何建议对我都有帮助。谢谢!

4

2 回答 2

18

尝试将“noexcept”添加到移动构造函数的声明中。

我不能引用标准,但最近版本的 gcc 似乎要求复制构造函数是公共的,或者将移动构造函数声明为“noexcept”。无论“noexcept”限定符如何,如果您将复制构造函数设为公开,它将在运行时按您预期的方式运行。

于 2012-07-15T02:25:08.833 回答
10

与上一个答案所建议的不同,gcc 4.7拒绝此代码是错误的,该错误已gcc 4.8 中得到纠正

完全符合标准的行为vector<T>::push_back是:

  • 如果只有一个复制构造函数而没有移动构造函数,push_back将复制它的参数,并给出强大的异常安全保证。也就是说,如果由于重新分配向量存储引发的异常而导致 push_back 失败,则原始向量将保持不变且可用。这是 C++98 中的已知行为,也是随后出现混乱的原因。
  • 如果 , 有一个noexcept移动构造函数Tpush_back将从它的参数移动,并给出强异常保证。这里没有惊喜。
  • 如果存在 noexcept存在的移动构造函数并且也存在复制构造函数,push_back则会复制对象并给予强大的异常安全保证。这乍一看是出乎意料的。虽然push_back可以移到这里,但这只能以牺牲强大的异常保证为代价。如果您将代码从 C++98 移植到 C++11 并且您的类型是可移动的,那将默默地改变现有push_back调用的行为。为了避免这个陷阱并保持与 C++98 代码的兼容性,C++11 回退到较慢的副本。这就是 gcc 4.7 行为的全部内容。但还有更多...
  • 如果存在移动构造函数,noexcept但根本没有复制构造函数——即元素只能移动而不能复制——push_back将执行移动但不会提供强大的异常安全保证。这就是 gcc 4.7 出错的地方。在 C++98 中,对于可移动但不可复制的类型没有push_backs。所以在这里牺牲强大的异常安全性并不会破坏现有的代码。这就是为什么它被允许并且原始代码实际上是合法的 C++11。

请参阅cppreference.compush_back

如果抛出异常,此函数无效(强异常保证)。

如果 T 的 move 构造函数不是 noexcept 并且无法访问复制构造函数,则 vector 将使用 throwing move 构造函数。如果它抛出,则放弃保证并且未指定效果。

或者来自 C++11 标准的更复杂的第 23.3.6.5 条(重点由我添加):

如果新大小大于旧容量,则导致重新分配。如果没有发生重新分配,则插入点之前的所有迭代器和引用仍然有效。如果异常被 T 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符或任何 InputIterator 操作引发,则没有任何影响。如果非 CopyInsertable T 的移动构造函数抛出异常,则未指定效果。

或者,如果您不喜欢阅读,Scott Meyer 的 Going Native 2013 演讲(从 0:30:20 开始,有趣的部分大约在 0:42:00)。

于 2013-09-17T20:00:34.737 回答