如何在向量增长时强制执行移动语义?

Ber*_*est 82 c++ resize vector move-semantics c++11

我有一个std::vector特定类的对象A.该类非常重要,并且定义了复制构造函数移动构造函数.

std::vector<A>  myvec;
Run Code Online (Sandbox Code Playgroud)

如果我用A对象填充向量(使用例如myvec.push_back(a)),则向量将增大,使用复制构造函数A( const A&)来实例化向量中元素的新副本.

我可以以某种方式强制执行类的移动构造函数A而不是使用它吗?

Joh*_*erg 115

您需要通知C++(特别是std::vector)您的移动构造函数和析构函数不会抛出,使用noexcept.然后在向量增长时调用移动构造函数.

这是如何声明和实现一个受以下因素尊重的移动构造函数std::vector:

A(A && rhs) noexcept { 
  std::cout << "i am the move constr" <<std::endl;
  ... some code doing the move ...  
  m_value=std::move(rhs.m_value) ; // etc...
}
Run Code Online (Sandbox Code Playgroud)

如果构造函数不是noexcept,std::vector则不能使用它,因为它不能确保标准所要求的异常保证.

有关标准中所述内容的更多信息,请阅读 C++ Move语义和异常

感谢Bo,他暗示这可能与例外情况有关.另请考虑Kerrek SB的建议和使用emplace_back.它可以更快(但通常不是),它可以更清晰,更紧凑,但也有一些陷阱(特别是对于非显式构造函数).

编辑,通常默认是您想要的:移动可移动的所有内容,复制其余内容.要明确要求,写

A(A && rhs) = default;
Run Code Online (Sandbox Code Playgroud)

这样做,你将在可能的情况下得到noexcept:默认的Move构造函数是否被定义为noexcept?

请注意,Visual Studio 2015及更早版本的早期版本不支持该版本,即使它支持移动语义.


Nik*_*nes 17

有趣的是,如果移动构造函数和析构函数都是,则gcc 4.7.2的向量仅使用移动构造函数noexcept.一个简单的例子:

struct foo {
    foo() {}
    foo( const foo & ) noexcept { std::cout << "copy\n"; }
    foo( foo && ) noexcept { std::cout << "move\n"; }
    ~foo() noexcept {}
};

int main() {
    std::vector< foo > v;
    for ( int i = 0; i < 3; ++i ) v.emplace_back();
}
Run Code Online (Sandbox Code Playgroud)

这输出了预期的:

move
move
move
Run Code Online (Sandbox Code Playgroud)

但是,当我删除noexcept~foo(),结果是不同的:

copy
copy
copy
Run Code Online (Sandbox Code Playgroud)

我想这也回答了这个问题.

  • 好点,但默认情况下析构函数是noexcept. (6认同)