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?
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.
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.
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.
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.
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.
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 S
s. 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With