Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::vector::emplace_back with lvalue expression

Does it ever make practical sense to use emplace_back with lvalue of some struct S:

like this:

std::vector<S> v;
auto s = S(/*...*/);
v.emplace_back(s);

Instead of just:

v.emplace_back(/* S constructor arguments */);

or is it just plain misuse of emplace_back, that only happen to work because const S& (and thus a copy constructor) is legitimate instantiation for Args... args inside emplace_back, and it is not forbidden explicitely?

like image 338
Student4K Avatar asked Mar 18 '19 13:03

Student4K


People also ask

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.

Is Emplace_back faster than Push_back?

With the simple benchmark here, we notice that emplace_back is 7.62% faster than push_back when we insert 1,000,000 object (MyClass) into an vector.

What is Emplace_back in vector?

vector::emplace_back()This function is used to insert a new element into the vector container, the new element is added to the end of the vector.

Does Emplace_back copy or move?

This code demonstrates that emplace_back calls the copy constructor of A for some reason to copy the first element. But if you leave copy constructor as deleted, it will use move constructor instead.


2 Answers

As you already said, passing const S& would just invoke the copy constructor.

Unless you intend to use s in some way before passing it to emplace_back, it is therefore not necessarily wise.

However, if the code to create s was, for instance, exceptionally long, it could improve readability to put it and the code for emplace_back on separate lines. Compilers are extremely good at optimizing such cases and will probably generate the same code anyways (if the copy constructor is default). Basic example: https://godbolt.org/z/D1FClE

If it improves readability or maintainability do it, otherwise there’s no value in it.

like image 164
idmean Avatar answered Oct 15 '22 03:10

idmean


If s is not needed later in the code, then it is a misuse of the emplace_back() function. This is because you are invoking the copy constructor of the S class instead of passing the arguments to the emplace_back() which will use the correct constructor from S.

Consider the following code:

#include <iostream>
#include <vector>

struct S
{
    S()          {std::cout<< "     default ctor" <<std::endl;}
    S(int)       {std::cout<< "     user-def ctor" <<std::endl;}
    S(const S &) {std::cout<< "     copy ctor" <<std::endl;}
    S(S &&)      {std::cout<< "     move ctor" <<std::endl;}
};

int main()
{
    std::vector<S> v;
    v.reserve(5);

    std::cout<< "auto calls: " <<std::endl;
    auto s = S();
    std::cout<<std::endl;

    std::cout<< "emplace_back( s ) calls: " <<std::endl;
    v.emplace_back(s);
    std::cout<<std::endl;

    std::cout<< "emplace_back( std::move(s) ) calls: " <<std::endl;
    v.emplace_back(std::move(s));
    std::cout<<std::endl;

    std::cout<< "emplace_back( S{} ) calls: " <<std::endl;
    v.emplace_back(S{});
    std::cout<<std::endl;

    std::cout<< "emplace_back( ) calls: " <<std::endl;
    v.emplace_back();
    std::cout<<std::endl;

    std::cout<< "emplace_back( 2 ) calls: " <<std::endl;
    v.emplace_back(2);
    std::cout<<std::endl;
}

The results are:

auto calls: 
     default ctor

emplace_back( s ) calls: 
     copy ctor

emplace_back( std::move(s) ) calls: 
     move ctor

emplace_back( S{} ) calls: 
     default ctor
     move ctor

emplace_back( ) calls: 
     default ctor

emplace_back( 2 ) calls: 
     user-def ctor

The reserve is used to allocate space for 5 Ss. Without reserving the space, the outputs would include additional calls to the copy ctors from the vector.

When you just pass the arguments to the constructor of S (in this case, nothing), the emplace_back() creates an S object using the default ctor directly inside the vector.

Btw, see the example in godbolt which is your friend in these cases to see exactly what happens in the background.

like image 42
Constantinos Glynos Avatar answered Oct 15 '22 04:10

Constantinos Glynos