当向量增长时如何强制移动语义?

发布于 2024-12-13 17:19:06 字数 326 浏览 2 评论 0原文

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

std::vector<A>  myvec;

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

我可以以某种方式强制使用类 A 的移动构造函数吗?

I have a std::vector of objects of a certain class A. The class is non-trivial and has copy constructors and move constructors defined.

std::vector<A>  myvec;

If I fill-up the vector with A objects (using e.g. myvec.push_back(a)), the vector will grow in size, using the copy constructor A( const A&) to instantiate new copies of the elements in the vector.

Can I somehow enforce that the move constructor of class A is beging used instead?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

骄兵必败 2024-12-20 17:19:06

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

这是声明和实现 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...
}

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

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

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

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

A(A && rhs) = default;

Doing that, you will get no except if possible: 默认的 Move 构造函数是否定义为 noexcept?

请注意,Visual Studio 2015 及更早版本的早期版本不支持这一点,尽管它支持移动语义。

You need to inform C++ (specifically std::vector) that your move constructor and destructor does not throw, using noexcept. Then the move constructor will be called when the vector grows.

This is how to declare and implement a move constuctor that is respected by 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...
}

If the constructor is not noexcept, std::vector can't use it, since then it can't ensure the exception guarantees demanded by the standard.

For more about what's said in the standard, read
C++ Move semantics and Exceptions

Credit to Bo who hinted that it may have to do with exceptions. Also consider Kerrek SB's advice and use emplace_back when possible. It can be faster (but often is not), it can be clearer and more compact, but there are also some pitfalls (especially with non-explicit constructors).

Edit, often the default is what you want: move everything that can be moved, copy the rest. To explicitly ask for that, write

A(A && rhs) = default;

Doing that, you will get noexcept when possible: Is the default Move constructor defined as noexcept?

Note that early versions of Visual Studio 2015 and older did not support that, even though it supports move semantics.

拧巴小姐 2024-12-20 17:19:06

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

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();
}

这输出了预期的结果:

move
move
move

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

copy
copy
copy

我想这也回答了 这个问题

Interestingly, gcc 4.7.2's vector only uses move constructor if both the move constructor and the destructor are noexcept. A simple example:

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();
}

This outputs the expected:

move
move
move

However, when I remove noexcept from ~foo(), the result is different:

copy
copy
copy

I guess this also answers this question.

幸福丶如此 2024-12-20 17:19:06

看来,强制 std::vector 在重新分配时使用移动语义的唯一方法(对于 C++17 及早期版本)是删除复制构造函数:)。通过这种方式,它将在编译时使用您的移动构造函数或尝试失败:)。

有许多规则规定 std::vector 不得在重新分配时使用移动构造函数,但没有规定在何处必须使用它。

template<class T>
class move_only : public T{
public:
   move_only(){}
   move_only(const move_only&) = delete;
   move_only(move_only&&) noexcept {};
   ~move_only() noexcept {};

   using T::T;   
};

直播

template<class T>
struct move_only{
   T value;

   template<class Arg, class ...Args, typename = std::enable_if_t<
            !std::is_same_v<move_only<T>&&, Arg >
            && !std::is_same_v<const move_only<T>&, Arg >
    >>
   move_only(Arg&& arg, Args&&... args)
      :value(std::forward<Arg>(arg), std::forward<Args>(args)...)
   {}

   move_only(){}
   move_only(const move_only&) = delete;   
   move_only(move_only&& other) noexcept : value(std::move(other.value)) {};    
   ~move_only() noexcept {};   
};

实时代码

您的 T类必须具有 noexcept 移动构造函数/赋值运算符和 noexcept 析构函数。否则你会得到编译错误。

std::vector<move_only<MyClass>> vec;

It seems, that the only way (for C++17 and early), to enforce std::vector use move semantics on reallocation is deleting copy constructor :) . In this way it will use your move constructors or die trying, at compile time :).

There are many rules where std::vector MUST NOT use move constructor on reallocation, but nothing about where it MUST USE it.

template<class T>
class move_only : public T{
public:
   move_only(){}
   move_only(const move_only&) = delete;
   move_only(move_only&&) noexcept {};
   ~move_only() noexcept {};

   using T::T;   
};

Live

or

template<class T>
struct move_only{
   T value;

   template<class Arg, class ...Args, typename = std::enable_if_t<
            !std::is_same_v<move_only<T>&&, Arg >
            && !std::is_same_v<const move_only<T>&, Arg >
    >>
   move_only(Arg&& arg, Args&&... args)
      :value(std::forward<Arg>(arg), std::forward<Args>(args)...)
   {}

   move_only(){}
   move_only(const move_only&) = delete;   
   move_only(move_only&& other) noexcept : value(std::move(other.value)) {};    
   ~move_only() noexcept {};   
};

Live code

Your T class must have noexcept move constructor/assigment operator and noexcept destructor. Otherwise you'll get compilation error.

std::vector<move_only<MyClass>> vec;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文