Reputation: 13876
If I want to add a new element to a vector there are a couple of ways I can do it:
struct ZeroInitialisedType
{
int a, b, c, d, e, f;
}
std::vector my_vector;
ZeroInitialisedType foo;
foo.a = 7;
//....
my_vector.push_back(foo);
The last question has got me thinking a bit about modern C++'s annoying quirks about value initialization. For example:
my_vector.emplace_back(); // PUSH A NEW OBJECT AT THE END
ZeroInitialisedType& ref = my_vector.back();
ref.a = 7;
//....
The problem with the above is that it zero initialises the new object on emplace_back(), then I write into it again. The same problem goes with this:
my_vector.emplace_back(ZeroInitialisedType());
// or
my_vector.push_back(ZeroInitialisedType());
If the element type of the vector isn't zero initialized then I assume the most efficient way would be to emplace_back with zero arguments, get a reference and write into it. However, when it's zero-initialized that's not the case and it becomes a problem.
Also, I'm aware that a hack could be to add a user-defined constructor to the class type, but that's not good at all to avoid zero-initialization.
Upvotes: 1
Views: 1299
Reputation: 23497
There are two independent questions related to your problem. The first one is whether an object of type ZeroInitialisedType
can be constructed such that some of its members are not zero-initialized. I believe this is not possible according to the aggregate-initialization rules. A simple demo is:
struct X { int a, b, c, d, e, f; };
void f(X* ptr)
{
new (ptr) X{7}; // the same with X(7) and X{.a=7}
}
Here, all members b
to f
are zero-initialized in f()
.
You can avoid this zeroing by adding the corresponding constructor; however, the class will no longer be an aggregate:
struct Y
{
Y(int a_) : a(a_) { }
int a, b, c, d, e, f;
};
void f(Y* ptr)
{
new (ptr) Y(7);
}
Live demo: https://godbolt.org/z/3aWooxWK7.
The second question is whether the object can be constructed directly in the vector's buffer. I tried some versions with libstdc++ and only the version with "empty" emplace_back
plus the back
call did that.
Live demo: https://godbolt.org/z/zavvrTGj6.
I guess the cause is in the possible reallocation, which is in our case accomplished by the _M_realloc_insert
call. It seems that those calls with non-empty template argument require the source to be in memory (the address is then passed via RDX
). Therefore, the addressed objects need to be first stored to the stack.
The version with empty emplace_back
does not have such requirements and succeeded with the direct construction in the vector's buffer. Of course, this analysis is GCC+libstdc++-related only.
Upvotes: 2
Reputation: 238321
I would suggest using following for inserting a small trivial aggregate type for readability:
my_vector.push_back({
.a = 7,
});
However, emplacing may technically more efficient:
my_vector.emplace_back(7);
Upvotes: 3