Can Std::Vector Emplace_Back Copy Construct from an Element of the Vector Itself

Can std::vector emplace_back copy construct from an element of the vector itself?

emplace_back is required to be safe for the same reason push_back is required to be safe; invalidation of pointers and references only has effect once the modifying method call returns.

In practice, this means that emplace_back performing a reallocation is required to proceed in the following order (ignoring error handling):

  1. Allocate new capacity
  2. Emplace-construct new element at the end of the new data segment
  3. Move-construct existing elements into new data segment
  4. Destruct and deallocate old data segment

At this reddit thread STL acknowledges failure of VC11 to support v.emplace_back(v[0]) as a bug, so you should definitely check whether your library supports this usage and not take it for granted.

Note that some forms of self-insertion are specifically prohibited by the Standard; for example in [sequence.reqmts] paragraph 4 Table 100 a.insert(p,i,j) has the prerequisite "i and j are not iterators into a".

how to use emplace_back to insert a new element into a vector of vector?

I can use push_back to insert a new element: data.push_back({1, 1}); In this way, I list initialized a new element, then a copy of this element is pushed to data?

exactly.

data.emplace(1, 1);

vector<Type>::emplace_back forwards its arguments to the constructor of Type. Now, std::vector<int> has a constructor that takes two integer arguments! The one specifying length (1) and fill element (1).

You can, however, inform the compiler you actually mean list initialization!

#include "fmt/format.h"
#include "fmt/ranges.h"
#include <initializer_list>
#include <vector>
int main() {
std::vector<std::vector<int>> a;
a.emplace_back(std::initializer_list<int>{1, 1});
a.emplace_back(std::initializer_list<int>{2, 2});
fmt::print("{}\n", fmt::join(a.begin(), a.end(), ";"));
}

yields the expected

 {1, 1};{2, 2}

To show it really does the in-place construction: Follow this link to gcc.godbolt.org, and observe how push_back({3,3,3,3}); actually calls the vector<int> constructor and then the insert of the vector<vector>, while emplace_back really just calls the initializer-list constructor.

C++11 - emplace_back between 2 vectors doesn't work

Although the existing answer provides a workaround using std::move that makes your program compile, it must be said that your use of emplace_back seems to be based on a misunderstanding.

The way you describe it ("I was trying to [...] moving the content from a vector to another one using emplace_back()") and the way you use it suggest that you think of emplace_back as a method to move elements into the vector, and of push_back as a method to copy elements into a vector. The code you use to fill the first instance of the vector seems to suggest this as well:

std::vector<obj> v;
for( int i = 0; i < 1000; ++i )
{
v.emplace_back(obj("Jon"));
}

But this is not what the difference between emplace_back and push_back is about.

Firstly, even push_back will move (not copy) the elements into the vector if only it is given an rvalue, and if the element type has a move assignment operator.

Secondly, the real use case of emplace_back is to construct elements in place, i.e. you use it when you want to put objects into a vector that do not exist yet. The arguments of emplace_back are the arguments to the constructor of the object. So your loop above should really look like this:

std::vector<obj> v;
for( int i = 0; i < 1000; ++i )
{
v.emplace_back("Jon"); // <-- just pass the string "Jon" , not obj("Jon")
}

The reason why your existing code works is that obj("Jon") is also a valid argument to the constructor (specifically, to the move constructor). But the main idea of emplace_back is that you need not create the object and then move it in. You don't benefit from that idea when you pass obj("Jon") instead of "Jon" to it.

On the other hand, in your second loop you are dealing with objects that were created before. There is no point in using emplace_back to move objects that exist already. And again, emplace_back applied to an existing object does not mean that the object is moved. It only means that it is created in-place, using the ordinary copy constructor (if that exists). If you want to move it, simply use push_back, applied to the result of std::move:

std::vector<obj> p;
for( int i = 0; i < 1000; ++i )
{
p.push_back(std::move(v[i])); // <-- Use push_back to move existing elements
}

Further notes
1) You can simplify the loop above using C++11 range-based for:

std::vector<obj> p;
for (auto &&obj : v)
p.push_back(std::move(obj));

2) Regardless of whether you use an ordinary for-loop or range-based for, you move the elements one by one, which means that the source vector v will remain as a vector of 1000 empty objects. If you actually want to clear the vector in the process (but still use move semantics to transport the elements to the new vector), you can use the move constructor of the vector itself:

std::vector<obj> p(std::move(v));

This reduces the second loop to just a single line, and it makes sure the source vector is cleared.

Is std::vector copying the objects with a push_back?

Yes, std::vector<T>::push_back() creates a copy of the argument and stores it in the vector. If you want to store pointers to objects in your vector, create a std::vector<whatever*> instead of std::vector<whatever>.

However, you need to make sure that the objects referenced by the pointers remain valid while the vector holds a reference to them (smart pointers utilizing the RAII idiom solve the problem).

Why does vectorT::emplace_back, such that T has a deleted copy constructor, fail to compile?

It works when you have move constructor:

#include <vector>

class Thing {
public:
Thing() {}
Thing(const Thing &) = delete;
Thing(Thing&&) = default;
};

int main() {
std::vector<Thing> v;
v.emplace_back();
return 0;
}

The type requirements of std::vector::emplace_back can provide more information.

why does emplace_back need move constructor

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.

push_back vs emplace_back to a std::vectorstd::string

I take a while to really understand what the advantage to use std::vector::emplace as aschepler said.

I found out the better scenario to use that is when we have our own class that receive some data when it is construct.

To make more clear, let's suppose we have:

  1. A vector of MyObject
  2. MyObject needs to receive 3 arguments to be constructed
  3. The functions get1stElem(), get2ndElem() and get3rdElem() provide the elements necessary to construct a MyObject instance

Then we can have a line like this:

vVec.emplace(get1stElem(), get2ndElem(), get3rdElem());

Then the std::vector::emplace will construct MyObject in place more efficiently than std::vector::push_back.

push_back vs emplace_back

In addition to what visitor said :

The function void emplace_back(Type&& _Val) provided by MSCV10 is non conforming and redundant, because as you noted it is strictly equivalent to push_back(Type&& _Val).

But the real C++0x form of emplace_back is really useful: void emplace_back(Args&&...);

Instead of taking a value_type it takes a variadic list of arguments, so that means that you can now perfectly forward the arguments and construct directly an object into a container without a temporary at all.

That's useful because no matter how much cleverness RVO and move semantic bring to the table there is still complicated cases where a push_back is likely to make unnecessary copies (or move). For example, with the traditional insert() function of a std::map, you have to create a temporary, which will then be copied into a std::pair<Key, Value>, which will then be copied into the map :

std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";

// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString)));

// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);

So why didn't they implement the right version of emplace_back in MSVC? Actually, it bugged me too a while ago, so I asked the same question on the Visual C++ blog. Here is the answer from Stephan T Lavavej, the official maintainer of the Visual C++ standard library implementation at Microsoft.

Q: Are beta 2 emplace functions just some kind of placeholder right now?

A: As you may know, variadic templates
aren't implemented in VC10. We
simulate them with preprocessor
machinery for things like
make_shared<T>(), tuple, and the new
things in <functional>. This
preprocessor machinery is relatively
difficult to use and maintain. Also,
it significantly affects compilation
speed, as we have to repeatedly
include subheaders. Due to a
combination of our time constraints
and compilation speed concerns, we
haven't simulated variadic templates
in our emplace functions.

When variadic templates are
implemented in the compiler, you can
expect that we'll take advantage of
them in the libraries, including in
our emplace functions. We take
conformance very seriously, but
unfortunately, we can't do everything
all at once.

It's an understandable decision. Everyone who tried just once to emulate variadic template with preprocessor horrible tricks knows how disgusting this stuff gets.



Related Topics



Leave a reply



Submit