在具有标准容器成员的类上实现移动操作的惯用方法不能是no异常
,因此不能通过像vector.push_back()
这样的操作进行移动。还是我弄错了?
加速
vector<Elem> data;
// ...
data.push_back( elem );
我们被鼓励进行移动操作noexcept
——这样在向量调整期间,库可以安全地将元素移动到重新分配的存储中。
class Elem {
// ...
Elem(Elem&&) noexcept; // noexcept important for move
Elem& operator=(Elem&&) noexcept; // noexcept important for move
};
到目前为止还不错,现在我的elem
可以更快地向后推了。
但是:如果我添加一个容器作为成员,我的类是否仍然可以标记为NoExceptMove?所有标准容器都没有移动noexcept
!
class Stuff {
vector<int> bulk;
// ...
Stuff(Stuff&& o) // !!! no noexcept because of vector-move
: bulk(move(o.bulk))
{}
Stuff& operator=(Stuff&&) // !!! no noexcept...
{ /* appropriate implementation */ }
};
这也意味着,我们也不能依赖编译器生成的移动操作,对吗?以下完整类也不会有noexcept
-move操作,因此不会“快速”,对吗?
struct Holder {
vector<int> bulk;
};
也许<代码>向量
这将对所有以容器为成员的数据结构产生重大影响。
我感觉到你的痛苦,真的。
一些std::实现将容器的移动成员标记为noexcept
,至少以分配器属性为条件,作为扩展。您可以调整代码以自动利用这些扩展,例如:
class Stuff {
std::vector<int> bulk;
// ...
public:
Stuff(Stuff&& o)
noexcept(std::is_nothrow_move_constructible<std::vector<int>>::value)
: bulk(std::move(o.bulk))
{}
Stuff& operator=(Stuff&&)
noexcept(std::is_nothrow_move_assignable<std::vector<int>>::value)
{ /* appropriate implementation */ }
};
你甚至可以测试你的类型是否有移动成员:
static_assert(std::is_nothrow_move_constructible<Stuff>::value,
"I hope Stuff has noexcept move members");
static_assert(std::is_nothrow_move_assignable<Stuff>::value,
"I hope Stuff has noexcept move members");
尤其是libc,只要分配器允许,它的所有容器都有noexcept移动成员,并且std::allocator
始终允许容器移动成员为noexcept。
这取决于标准容器正在使用的分配器。默认分配器std::allocator
与不推荐使用的throw()
相比,noexcept
的一个有趣特性是,您可以使用在编译时计算的表达式。要测试的确切条件可能不是微不足道的,但是。。。