C++ 如果类移动操作包含标准容器,是否可以将其标记为noexcept?

C++ 如果类移动操作包含标准容器,是否可以将其标记为noexcept?,c++,c++11,containers,move-semantics,noexcept,C++,C++11,Containers,Move Semantics,Noexcept,在具有标准容器成员的类上实现移动操作的惯用方法不能是noexcept,因此不能通过vector.push_back()之类的操作进行移动。还是我错了 加速 vector<Elem> data; // ... data.push_back( elem ); 到目前为止一切都很好,现在我的elems可以向后推得更快 但是:如果我添加一个容器作为成员,我的类是否仍然可以标记为NoExceptMove?所有标准容器都没有移动noexcept class Stuff { vector

在具有标准容器成员的类上实现移动操作的惯用方法不能是
noexcept
,因此不能通过
vector.push_back()
之类的操作进行移动。还是我错了

加速

vector<Elem> data;
// ...
data.push_back( elem );
到目前为止一切都很好,现在我的
elem
s可以向后推得更快

但是:如果我添加一个容器作为成员,我的类是否仍然可以标记为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 */ }
};
也许
vector
移动起来有点太简单了,但是
vector


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

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


与不推荐使用的
throw()
相比,
noexcept
的一个有趣特性是,您可以使用在编译时计算的表达式。要测试的确切情况可能不是微不足道的…

我真的感觉到了你的痛苦

一些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 */ }
};
类内容{
std::载体体积;
// ...
公众:
Stuff(Stuff&&o)
noexcept(std::is\u nothrow\u move\u constructible::value)
:散装(标准::移动(散装))
{}
填充和运算符=(填充和)
noexcept(std::is_nothrow_move_assignable::value)
{/*适当的实施*/}
};
您甚至可以测试您的类型是否具有noexcept move成员:

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");
static\u assert(std::is\u nothrow\u move\u constructible::value,
“我希望Stuff不会有例外”(move成员);
静态断言(std::is \u nothrow\u move\u assignable::value,
“我希望Stuff不会有例外”(move成员);

特别是,只要分配器允许,它的所有容器都有noexcept移动成员,
std::allocator
总是允许容器移动成员为noexcept。

问题的一部分是分配器特性的未知。您可能认为
std::vector::swap()
也可以是
noexcept
。即使标准容器的成员函数没有必需的noexcept规范,它们也有异常保证。如果这些保证说操作不会抛出,您可以“安全地”进行自己的移动。好的,谢谢。编译器为
struct Holder生成什么?我想那门课会有一个无效的移动,即复制一个?我想,编译器不会包含一个有条件的
noexcept
。(如果我继续斯科特的笑话,我会问是否有可下载的二进制文件。但我不会;-)实际上,
Holder
noexcept
规范将完全像我建议你对
东西所做的那样。实际上,您也可以只使用
=default
。您可以使用与我为
Stuff
所展示的完全相同的方式来测试
Holder
noexcept
规范。具体地说,如果
向量
的移动构造函数是
noexcept
,那么
Holder
的隐式移动构造函数也将是
noexcept
。我放心了。我想这就是它的许多单词所表达的意思。@HowardHinnant测试可能有点误导,因为它还可以捕获
noexcept
copy构造函数;如果要使用复制构造函数进行移动,请参见上的注释,
是\u nothrow\u move\u
特征仍然是提出问题的正确工具(通过设计)。
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");