Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why does emplace_back need move constructor

Tags:

c++

c++11

I have the following code:

#include <string>
#include <vector>
#include <iostream>

class Test final {
public:
  Test(const std::string& s)
    : s_(s) {
    std::cout << "constructing: " << s_ << std::endl;
  }
#ifdef NO_MOVE
private:
  Test(const Test& t) = delete;
  Test(Test&& t) = delete;
#else
public:
  Test(const Test& t)
    : s_(t.s_) {
    std::cout << "copying: " << s_ << std::endl;
  };
  Test(Test&& t)
    : s_(std::move(t.s_)) {
    std::cout << "moving: " << s_ << std::endl;
  };
#endif
private:
  std::string s_;
};

int main() {
  std::vector<Test> v;
  v.emplace_back("emplace_back");
}

When a move constructor is allowed, the following occurs:

[matt test] g++ -std=c++11 main.cpp && ./a.out
constructing: emplace_back

However, if the move constructor is removed:

[matt test] g++ -std=c++11 main.cpp -DNO_MOVE && ./a.out
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = Test; _Args = {Test}]’:
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:77:3:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<Test*>; _ForwardIterator = Test*; bool _TrivialValueTypes = false]’
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:119:41:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::move_iterator<Test*>; _ForwardIterator = Test*]’
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:260:63:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::move_iterator<Test*>; _ForwardIterator = Test*; _Tp = Test]’
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_uninitialized.h:283:69:   required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = Test*; _ForwardIterator = Test*; _Allocator = std::allocator<Test>]’
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/vector.tcc:410:6:   required from ‘void std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) [with _Args = {const char (&)[13]}; _Tp = Test; _Alloc = std::allocator<Test>]’
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/vector.tcc:102:4:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {const char (&)[13]}; _Tp = Test; _Alloc = std::allocator<Test>]’
main.cpp:32:32:   required from here
main.cpp:14:3: error: ‘Test::Test(Test&&)’ is private
In file included from /usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/vector:63:0,
                 from main.cpp:2:
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h:77:7: error: within this context
/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_construct.h:77:7: error: use of deleted function ‘Test::Test(Test&&)’
main.cpp:14:3: error: declared here

But the emplace_back doesn't use the move constructor. Why does the initialization require a move constructor in this instance?

like image 240
Matt Clarkson Avatar asked Mar 25 '13 13:03

Matt Clarkson


People also ask

Does Emplace_back use move constructor?

Calling emplace_back will call the move constructor of std::string when std::move is used, which could save on a copy (so long as that string isn't stored in a SSO buffer). Note that this is essentially the same as push_back in this case.

Should I use std :: move with Emplace_back?

emplace_back(std::move(w)); I recommend sticking with push_back for day-to-day use. You should definitely use emplace_back when you need its particular set of skills — for example, emplace_back is your only option when dealing with a deque<mutex> or other non-movable type — but push_back is the appropriate default.

Why is Emplace_back faster than Push_back?

because emplace_back would construct the object immediately in the vector, while push_back , would first construct an anonymous object and then would copy it to the vector.

Should I use Push_back or Emplace_back?

Specific use case for emplace_back : If you need to create a temporary object which will then be pushed into a container, use emplace_back instead of push_back . It will create the object in-place within the container. Notes: push_back in the above case will create a temporary object and move it into the container.

Why does emplace_back call a move constructor?

Just for the sake of completeness, another reason why emplace_back might call a move constructor is: emplace_back may cause the vector to grow, and thus move its initial contents to their new memory location. This is not the problem here, because calling reserve guarantees enough capacity, but generally it's an answer to the question.

Why does emplace_back require moveinsertable for vectors?

If T 's move constructor is not noexcept and is not CopyInsertable into *this, vector will use the throwing move constructor. If it throws, the guarantee is waived and the effects are unspecified. Since reallocation may take place, emplace_back requires the element type to be MoveInsertable for vectors.

How does emplace_back work?

emplace_back forwards its arguments to the constructor of the vector element class, called in-place on the next available position of the vector. That's why you are getting a regular constructor call followed by a move constructor call.

How to append a new object with emplace_back?

If you want to append a new object with emplace_back, just call: Just for the sake of completeness, another reason why emplace_back might call a move constructor is: emplace_back may cause the vector to grow, and thus move its initial contents to their new memory location.


2 Answers

As specified in the comment after the question. The emplace_back operator may need to reallocate the containers memory and as such the vector template type needs to be either copy or move constructable.

It's not the forwarding of the arguments that is the issue it is the allocating of memory for the new object.

like image 200
Matt Clarkson Avatar answered Oct 18 '22 02:10

Matt Clarkson


If there is no space in the vector then it should allocate new space and move everything there and to avoid copy of the resource contained or owned by the objects(inside vector) move is required

For vector of type Test

Test object(original)--->resource on heap
Test object(relocated with move constructor)------>resource on heap
Test object(relocated with copy constructor)------>copy of resource on heap
like image 38
Abhijit-K Avatar answered Oct 18 '22 01:10

Abhijit-K