vector::pushback坚持使用复制构造函数,尽管提供了移动构造函数
vector::push_back insists on using copy constructor though a move constructor is provided
我从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=%dn", other.th );
}
const branch& operator=( branch&& other )
{
printf( "called! other.th=%dn", other.th );
return (*this);
}
};
int main()
{
vector<branch> v;
branch a;
v.push_back( std::move(a) );
return 0;
}
我希望这段代码能够编译,但它在gcc中失败了。事实上,gcc抱怨"branch::branch(constbranch&)是私有的",据我所知,它不应该被调用。
赋值运算符有效,因为如果我用替换main()的主体
branch a;
branch b;
b = a;
它将按预期编译和运行。
这是gcc的正确行为吗?如果是,上面的代码有什么问题?任何建议对我都有帮助。谢谢!
尝试将"noexcept"添加到move构造函数的声明中。
我不能引用标准,但最近版本的gcc似乎要求复制构造函数是公共的,或者移动构造函数声明为"noexcept"。不管"noexcept"限定符是什么,如果您将复制构造函数公开,它在运行时的行为将与您预期的一样。
与前面的答案不同,gcc 4.7拒绝此代码是错误的,此错误已在gcc 4.8中更正。
vector<T>::push_back
的完全符合标准的行为是:
- 如果只有复制构造函数而没有移动构造函数,则
push_back
将复制其参数,并提供强大的异常安全保证。也就是说,如果push_back由于向量存储的重新分配触发的异常而失败,则原始向量将保持不变并可用。这是C++98中已知的行为,也是造成随后混乱的原因 - 如果
T
有一个noexcept
移动构造函数,push_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_back
。因此,在这里牺牲强异常安全性并不会破坏现有代码。这就是为什么它是允许的,并且原始代码实际上是合法的C++11
参见push_back
:上的cppreference.com
如果抛出异常,则此函数无效(strong例外担保)。
如果T的move构造函数不是noexcept并且复制构造函数不可访问,vector将使用抛出移动构造函数。如果它抛出,担保将被放弃,其影响为未指明。
或者C++11标准中更复杂的§23.3.6.5(我补充了重点):
如果新容量大于旧容量,则会导致重新分配。如果没有重新分配,则之前的所有迭代器和引用插入点仍然有效。如果引发的异常不是由复制构造函数、移动构造函数、赋值运算符或将T的赋值运算符或通过任何InputIterator操作移动到那里没有任何影响如果一个非CopyInsertable T,效果未指定
或者,如果你不喜欢阅读,Scott Meyer的《走向本土》2013演讲(从0:30:20开始,有趣的部分在0:42:00左右)。
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- std::vector::p ush_back() 不会在 MSVC 上编译具有已删除移动构造函数的对象
- 仅包含可移动 std::map 的类的移动构造函数不起作用
- 为什么调用复制构造函数而不是移动构造函数?
- 基类中的默认析构函数禁用子类中的移动构造函数(如果有成员)
- 从具有按值捕获的 lambda 移动构造 std::函数时,移动构造函数调用两次
- 具有已删除移动和复制构造函数的类的就地构造
- 移动构造函数和右值引用
- 使用移动调用对等构造函数unique_ptr默认构造函数
- 为什么 std::memmove 中联合的默认非平凡移动构造函数C++?
- 具有专用化的模板类中的可靠条件复制和移动构造函数
- 构造函数采用std::string_view与std::string并移动
- C++:为什么不调用移动构造函数?
- 了解构造函数在移动、复制、赋值语义中的行为
- 没有移动的构造函数移动课程
- 引用绑定和复制构造函数/移动构造函数
- 构造函数移动
- C++ 向量实现 - 移动构造函数 - 移动与前进
- 我真的必须取消移动构造函数/移动结构中的所有成员还是只是指针
- 将类(没有默认构造函数)移动到另一个类的move构造函数中