7

If I have a struct in which I did not provide any copy and move constructor:

struct MyStruct {
  MyStruct() { // this is the only function
    ...
  }
  ...
};

then if I do the following:

std::vector<MyStruct> vec;
...
vec.push_back(MyStruct());

instead of using std::move() like the followings:

vec.push_back(std::move(MyStruct()));

Will c++11 smartly do the move for my temporary variable? Or, how can I make sure it is a move instead of a copy?

David G
  • 94,763
  • 41
  • 167
  • 253
keelar
  • 5,814
  • 7
  • 40
  • 79
  • Does `MyStruct` have a move constructor, or not ? It may either be user-declared (explicit) or automatically generated under some conditions (implicit), and of course only movable structures can be moved. – Matthieu M. Aug 06 '14 at 08:20
  • What @MatthieuM. said. Exactly the point I was trying to make in my answer. – juanchopanza Aug 06 '14 at 08:21
  • It still depends :-) It also depends on what you mean by "`std::move` being explicitly used". You should also clarify that. – juanchopanza Aug 06 '14 at 08:36
  • Improved the question once again. Hopefully it looks better now. – keelar Aug 06 '14 at 08:42
  • OK, the use of `std::move` makes no difference here. I added that to my answer. – juanchopanza Aug 06 '14 at 08:52
  • @keelar: Note: a move constructor may only be generated automatically if each and every attribute can be moved. Not movable types include user-defined types disabling move (whatever the way), references and `const` types for example. – Matthieu M. Aug 06 '14 at 08:52
  • Not sure if he / she is still here, but to the one who down vote this question: does the updated question address the issues in your mind or look better? – keelar Aug 06 '14 at 08:56

5 Answers5

11

In C++11 std::vector::push_back will use a move constructor if passed an rvalue (and a move constructor exists for the type), but you should also consider using std::vector::emplace_back in such situations; std::vector::emplace_back will construct the object in place rather than moving it.

dustyrockpyle
  • 3,184
  • 17
  • 12
  • 1
    The struct will have an implicitly defined move constructor if it doesn't define any of the members that suppress it - in a C++11 compliant compiler. Unfortunately, as of yet, in this regard Visual C++ isn't; thus requiring you to explicitly define one. – heinrichj Aug 06 '14 at 07:44
  • +1 recommending `emplace_back` is sound - if post-optimisation an object has to be `move`-d it's still copying many types of data members - it's mainly types with pointers to dynamic memory that become cheap and constant time (but not quite free) to move (e.g. Standard containers). – Tony Delroy Aug 06 '14 at 08:04
  • 3
    As it stands the answer is misleading. For a "move constructor" to be used, the type needs to have one in the first place. It is not simply a matter of passing an rvalue reference. – juanchopanza Aug 06 '14 at 08:20
  • 2
    It should say "if handed an rvalue". References types of variables, never of expressions. – Kerrek SB Aug 06 '14 at 09:00
  • @juanchopanza I added a clarification pointing out a move constructor needs to exist for it to be called, which isn't obvious if you're new to move semantics. – dustyrockpyle Aug 06 '14 at 10:48
7

Will c++11 smartly do the move for my temporary variable? Or, how can I make sure it is a move instead of a copy?

It depends. This

vec.push_back(MyStruct());

will bind to

std::vector<MyStruct>::push_back(MyStruct&&);

but whether the rvalue passed is moved or copied depends fully on whether MyStruct has a move copy constructor (likewise for move assignment).

It will make absolutely no difference if you call

vec.push_back(std::move(MyStruct()));

because MyStruct() is already an rvalue.

So it really depends on the details of MyStruct. There is simply not enough information in your question to know if your class has move constructor.

These are the conditions that must be met for a class to have an implicitly generated move constructor:

  • no user-declared copy constructors
  • no user-declared copy assignment operators
  • no user-declared move assignment operators
  • no user-declared destructors

Of course, you can always provide your own if any of these conditions are not met:

MyStruct(MyStruct&&) = default;
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • Thanks your answer! Can you point me to the reference page about the conditions for having an implicitly generated move constructor? – keelar Aug 06 '14 at 08:31
  • 1
    @keelar I have previously [distilled this from the C++11 standard](http://stackoverflow.com/questions/21013430/default-copy-constructor-and-assignment-for-class-with-move-constructor-and-assi), but there is a reference [**here**](http://en.cppreference.com/w/cpp/language/move_constructor). I haven't cross-checked whether this completely covers what the standard lays out, because there are some confusing corner cases and some details may have changed for C++1y. See also [this and related questions](http://stackoverflow.com/questions/13174973/when-should-compiler-generate-move-constructor). – juanchopanza Aug 06 '14 at 08:35
  • 2
    I think the `push_back` call will always bind to `std::vector::push_back(MyStruct&&);` for an rvalue. However, if no move constructor is available, the construction of the new element in the vector will fall back to the copy ctor inside `push_back` (or become ill-formed if the move ctor is explicitly deleted). – T.C. Aug 06 '14 at 09:22
5

Because the MyStruct() will create an rvalue the T && overload will be called.

It's actually very easy to verify (demo):

#include <iostream>
struct A{ int x; };

void  foo(A &&x){ std::cout<<"&&" <<std::endl; }

void  foo(A &x){ std::cout<<"&" <<std::endl; }

int main() {
    foo(A()); // prints &&
    A a;
    foo(a); // prints &
    return 0;
}

To clarify: I didn't mention anything about move constructor because one can have an explicitly deleted move constructor and still the T && will be invoked.

E.g (demo):

#include <iostream>
struct A{ int x; A() = default; A(const A& ) = default; A(A&&) = delete; };
/*                                                                  ^
                                                            no move ctor */
void  foo(A &&x){ std::cout<<"&&" <<std::endl; }

void  foo(A &x){ std::cout<<"&" <<std::endl; }

int main() {
    foo(A()); //still prints &&
    A a;
    foo(a);
    return 0;
}

As I said before this is because it's an rvalue...

Scis
  • 2,934
  • 3
  • 23
  • 37
  • 1
    It is not because the overload `A&&` is taken than the move constructor is used (http://ideone.com/JUMBqs) – Jarod42 Aug 06 '14 at 08:25
  • Both Q/A are unclear about *moving*, Does that means cast to `T&&` (and there is no need to cast rvalue that way) ? Does it mean move construct/assign ? Any can be used without the other, that is what I want to clarify with my remark. – Jarod42 Aug 06 '14 at 10:30
1

Yes, it is going to use push_back( T&& value ), and move the value.

BЈовић
  • 62,405
  • 41
  • 173
  • 273
  • Not if the type doesn't have a move constructor. – juanchopanza Aug 06 '14 at 08:12
  • 1
    In which case `std::move` wouldn't help, either. The question is whether `std::move` makes a difference, so that pretty much assumes a move ctor is available. – MSalters Aug 06 '14 at 08:27
  • @MSalters That is not my interpretation of the question. OP doesn't mention `std::move`, and from other comments, I think they weren't fully aware of when a type is movable. – juanchopanza Aug 06 '14 at 08:31
  • @MSalters Ha! I totally missed that. I got used to SO titles having little connection to the question being asked so I must have unconsciously filtered it out. – juanchopanza Aug 06 '14 at 08:37
1

If the type is movable then it definitely will be. In general standard-complying compiler should always choose move semantics to copy semantics if such are available.

Red XIII
  • 5,771
  • 4
  • 28
  • 29
  • Thank you. Can I also know in general which type is movable and which type is not? – keelar Aug 06 '14 at 07:42
  • 1
    @keelar A type is moveable if it has defined move constructor/move assignment operator. Of course you - as the class - creator has to know whether or not you can perform "destructive copy" on your type or not. Defining (or =default declaring) move member functions is NOT required if your class can have them created by compiler. Take a look at http://en.cppreference.com/w/cpp/language/move_constructor and http://en.cppreference.com/w/cpp/language/move_operator. – Red XIII Aug 06 '14 at 17:12