What is the usecase of return value for std::vector::emplace_back in C++17?

1.2k Views Asked by At

I have read in cppreference.com that the new(since C++17) std::vector::emplace_back has an return value of referance to the inserted element.

Return value

  • (none) (until C++17)
  • A reference to the inserted element. (since C++17)

I was thinking, while inserting element to the vector, why we need a referance to it? how this could be usful or what is the usecase case of this new return?

Here is a sample code which I wrote to see, the feature.

#include <vector>

int main()
{
    std::vector<int> myVec;
    for(int i = 0; i < 3; ++i)
    {
        int& newElement = myVec.emplace_back(i);
        ^^^^^^^ => why standard should expose the element after inserting.
    }
}
3

There are 3 best solutions below

0
On BEST ANSWER

The change is made by P0084. The motivation the author gives is

I often find myself wanting to create an element of a container using emplace_front or emplace_back, and then access that element, either to modify it further or simply to use it. So I find myself writing code like this:

my_container.emplace_back(...);
my_container.back().do_something(...);

Or perhaps:

my_container.emplace_back(...);
do_something_else(my_container.back());

Quite a common specific case is where I need to construct an object before I have all the information necessary to put it into its final state, such as when I’m reading it from a file:

my_container.emplace_back(); // Default construct.
my_container.back().read(file_stream); // Read the object.

This happens often enough that I tend to write little templates that call some version of emplace and return back, which seems rather unnecessary to me. I believe the emplace_front and emplace_back functions should return a non-const reference to the newly created element, in keeping with the current Standard Library trend of returning useful information when practical. It was an oversight (on my part) in the original emplace proposal that they do not.

3
On
std::cout << vec.emplace_back(7);

it is just convience so you can do more than one thing in an expression.

vec.emplace_back().reg();

anything done by it can be replicated by

(void(vec.emplace_back()), vec.back()).reg();

in pre-C++17 versions (void here is future-proofing parnoia)

It is a common pattern to create an object and use it right away; the return value makes it slightly easier.

0
On

CRASH alert! Be very careful using a (returned) reference, especially after you add another element. The vector then tends to reallocate the memory and the firstly returned reference isn't valid any more, resulting in a memory exception! Example:

std::vector <int> intVector;

int& a0 = intVector.emplace_back(0);
int& a1 = intVector.emplace_back(1);  // a0& invalid
int& a2 = intVector.emplace_back(2);  // a0& and a1& invalid

If you look at the vector itself at the end, everything seems to be fine {0,1,2}. If you look into a0 and a1, it's just rubbish. Reason: de vector has been reallocated, leaving the references pointing to nowhere. BTW: also using the back() function can bring you in trouble for the same reason.