gcc vector::push_back坚持使用复制构造函数,尽管提供了移动构造函数

ijxebb2r  于 2023-10-19  发布在  其他
关注(0)|答案(2)|浏览(102)

我收到一个奇怪的错误从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抱怨“分支::分支(const分支&)是私有的”,据我所知,这不应该被调用。
赋值操作符可以工作,因为如果我将main()的主体替换为

branch a;
branch b;
b = a;

它将按预期编译和运行。
这是gcc的正确行为吗?如果是,那么上面的代码有什么问题?任何建议对我都有帮助。谢谢你,谢谢!

8ehkhllq

8ehkhllq1#

尝试将“noexcept”添加到移动构造函数的声明中。
我不能引用标准,但最近的gcc版本似乎要求复制构造函数是公共的,或者移动构造函数被声明为“noexcept”。不管“noexcept”限定符是什么,如果您将复制构造函数设置为公共的,它将在运行时按照您的预期运行。

vsikbqxv

vsikbqxv2#

与前面的答案不同,gcc 4.7拒绝这个代码是错误的,错误是corrected in gcc 4.8
vector<T>::push_back的完全符合标准的行为是:

  • 如果只有复制构造函数而没有移动构造函数,push_back将复制其参数并给予给予强异常安全保证。也就是说,如果push_back由于向量存储的重新分配触发的异常而失败,则原始向量将保持不变并可用。这是C++98中已知的行为,也是随后混乱的原因。
  • 如果T有一个noexcept move构造函数,push_back将从它的参数 move,并将给予强异常保证。没有意外
  • 如果有一个移动构造函数是 notnoexcept,并且还有一个复制构造函数,push_back将 * 复制 * 对象并给予强异常安全保证。乍一看,这是出乎意料的。虽然push_back可以移动到这里,但这只能以牺牲强大的异常保证为代价。如果你将代码从C98移植到C11,并且你的类型是可移动的,那么这将悄悄地改变现有push_back调用的行为。为了避免这个缺陷并保持与C98代码的兼容性,C11福尔斯又回到了较慢的副本。这就是gcc 4.7行为的全部内容。但还有更多...
  • 如果有一个不是noexcept的移动构造函数,但根本没有复制构造函数-也就是说,元素只能移动而不能复制-push_back将执行移动,但 * 不会 * 给予强异常安全保证。这就是GCC 4.7出错的地方。在C98中,对于可移动但不可复制的类型,没有push_back s。因此,在这里牺牲强异常安全性并不会破坏现有代码。这就是为什么它是允许的,并且原始代码实际上是法律的C11。

参见push_back上的cppreference.com
如果抛出异常,则此函数不起作用(强异常保证)。
如果T的移动构造函数不是noexcept,并且复制构造函数不可访问,则vector将使用抛出移动构造函数。如果抛出,则放弃担保,影响未指定。
或者从C++11标准的第23.3.6.5节(强调是我加的):
如果新容量大于旧容量,则导致重新分配。如果没有发生重新分配,则插入点之前的所有迭代器和引用仍然有效。如果异常不是由T的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符或任何InputIterator操作引发的,则不会产生任何影响。如果非CopyInsertable T的move构造函数抛出异常,则效果未指定。
或者如果你不喜欢阅读,Scott Meyer's Going Native 2013 talk(从0:30:20开始,有趣的部分大约在0:42:00)。

相关问题