9

在具有标准容器成员的类上实现移动操作的惯用方式不能noexcept也因此不能通过诸如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的 s 可以更快地推回。

但是:如果我添加一个容器作为成员,我的班级仍然可以标记为 noexcept-move 吗?所有标准容器都没有它们的动作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-operations,因此不是“快”的,对吗?

struct Holder {
    vector<int> bulk;
};

也许vector<int>移动起来有点太简单了,但是呢vector<Elem>

这将对所有以容器为成员的数据结构产生重大影响。

4

2 回答 2

9

我感觉到你的痛苦,真的。

一些 std::implementations 会将容器的移动成员标记为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 */ }
};

你甚至可以测试你的类型是否有 noexcept 移动成员:

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。

于 2014-01-23T23:11:30.340 回答
2

这取决于您的标准容器正在使用的分配器。默认分配器std::allocator<T>保证不会在复制时抛出(并且没有移动构造函数),这反过来意味着容器不会在移动时抛出。

noexcept与不推荐使用的相比,一个有趣的功能throw()是您可以使用在编译时评估的表达式。要测试的确切条件可能并非微不足道...

于 2014-01-23T23:01:36.460 回答