110

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?

JFMR
  • 23,265
  • 4
  • 52
  • 76
Bertwim van Beest
  • 1,311
  • 2
  • 9
  • 7

3 Answers3

152

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.

Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • Out of interest, how _does_ the impl "know" whether the `value_type`'s move ctor is `noexcept`? Perhaps the language restricts the function call candidate set when the calling scope is also a `noexcept` function? – Lightness Races in Orbit Apr 11 '13 at 11:21
  • 1
    @LightnessRacesinOrbit I assume it's just doing something such as http://en.cppreference.com/w/cpp/types/is_move_constructible. There can be only one move constructor so it should be clearly defined by the declaration. – Johan Lundberg Apr 11 '13 at 13:48
  • @LightnessRacesinOrbit, I've since learned that there is no (standard/useful) way to really know if there is a `noexcept` move constructor. `is_nothrow_move_constructible` will be true if there is a `nothrow` copy constructor. I'm not aware of any real case of expensive `nothrow` copy constructors so it's not clear that it really matters. – Johan Lundberg Jan 10 '16 at 09:01
  • Doesn't work for me. My destructor, move constructor, and move assigment functions are all marked `noexcept` in both header and implementation, and when I do a push_back(std:;move) it still calls the copy constructor. I am tearing my hair out here. – AlastairG Jul 26 '19 at 10:13
  • @alastairg please post a reproduce example for example at ideone. Perhaps you try to move from a const value or const reference? That gives you a copy. – Johan Lundberg Jul 26 '19 at 10:21
  • 1
    @Johan I found the problem. I was using `std::move()` on the wrong `push_back()` call. One of those times when you are looking so hard for a problem that you don't see the obvious error right in front of you. And then it was lunchtime and I forgot to delete my comment. – AlastairG Jul 26 '19 at 12:52
  • But what if both the move ctor and the copy ctor can throw exceptions? – einpoklum Feb 19 '20 at 22:18
18

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.

JFMR
  • 23,265
  • 4
  • 52
  • 76
Nikola Benes
  • 2,372
  • 1
  • 20
  • 33
  • It seems to me that the other answers only talk about the move constructor, not about the _destructor_ having to be noexcept. – Nikola Benes Mar 18 '13 at 11:29
  • Well, it should be, but as it turns out, in gcc 4.7.2 it wasn't. So this problem was, in fact, specific to gcc. It should be fixed in gcc 4.8.0, though. See [related stackoverflow question](http://stackoverflow.com/questions/15721544/destructors-and-noexcept). – Nikola Benes May 31 '13 at 10:42
-1

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;
tower120
  • 5,007
  • 6
  • 40
  • 88
  • 1
    It is not necessary to delete copy constructor. If move constructor is noexcept, it will be used. – balki Dec 28 '17 at 19:29
  • @balki It MAY be used. Standard does not REQUIRE this now. Here is discussion https://groups.google.com/a/isocpp.org/forum/?utm_medium=email&utm_source=footer#!msg/std-proposals/j5URs5ZY3GI/AanXG977CAAJ – tower120 Jan 01 '18 at 15:30