Emplace an aggregate in std::vector
std::vector::emplace
expects an iterator as argument too, because it inserts the element before that iterator's position.
Another problem is that your {i, 0.0, 0.0, 1}
initialization doesn't work because it isn't in a context which tells what type it needs to instantiate. The reason there isn't any context is due to emplace
and emplace_back
member functions having generic parameters.
If you just want to append elements to the vector, use emplace_back
.
However, emplace_back
depends on the element type having a valid constructor in order to work, as the element is initialized through parentheses. That changed in C++20, which now allows aggregate-initialization through parentheses without the need to define a valid constructor.
So, up until C++17, your example would be changed to:
for (int i = 0; i < num_particles; ++i)
particles.push_back({i, 0.0, 0.0, 1});
And in C++20 and later, you may do this instead:
for (int i = 0; i < num_particles; ++i)
particles.emplace_back(i, 0.0, 0.0, 1);
C++11 emplace_back on vectorstruct?
For anyone from the future, this behavior will be changed in C++20.
In other words, even though implementation internally will still call T(arg0, arg1, ...)
it will be considered as regular T{arg0, arg1, ...}
that you would expect.
How to emplace to a std::vector of std::array?
std::array
doesn't have any constructors, it's initialization follows aggregate initialization.
You have two options, either use push_back
instead of emplace_back
with a brace-init-list:
voa.push_back({ 0, 0 });
Or you can use std::array
's (implicit) copy and move constructors and construct a temporary std::array
:
voa.emplace_back(std::array<int>{0, 0}); // pre C++17
voa.emplace_back(std::array{0, 0}); // C++17
Ultimately both methods should generate the same machine code.
In response to your edit:
std::array<int, 2> a = {0, 0};
is NOT the same as
std::array<int, 2> a = std::initializer_list<int>{0, 0};
{0, 0}
is a brace-init-list, which doesn't have a type. This is why you can't use them as an argument to a templated function. See this question for more details.
What's the most efficient way to emplace a new element in a vector?
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.
std::vector::emplace_back with a POD C++
emplace_back
uses parentheses to initialize the value, which means a constructor is required to use it until C++20. C++20 introduced parenthesized initialization of aggregates, so you code becomes valid as is.
Until then you can just do
vec.push_back({1.0f, 2.0f, 3.0f, 4.0f});
Is it possible to emplace a std::array in a container? If it is how? If not, why?
Let me try to clarify a bit. First of, std::array<>
is an aggregate. It does not have any user-defined constructors at all.
So it is quasi-movable (technical term is trivially movable), meaning you can construct it from temporary, but it is going to simply copy the underlying array. So what the code ends up doing is copying supplied array into the newly allocated vector element.
You would achieve identical results (but will do slightly less typing) if you replace the emplace_back
with push_back
like following:
v.push_back({1,2,3});
And emplace_back(1, 2, 3)
doesn't compile because std::array<>
does not have any constructors which take more than one argument.
vector's emplace_back - vector as a constructor argument
You forgot another pair of braces for the std::vector
. Also, you need to tell emplace_back()
what kind of arguments you pass it, so you need to invoke std::vector
's constructor:
vecC.emplace_back(std::vector{ B{ std::make_shared<A>(), 2 } }, false);
Alternatively, don't use emplace_back()
and use push_back()
instead:
vecC.push_back({{{std::make_shared<A>(), 2}}, false});
Related Topics
When to Mark a Function in C++ as a Virtual
a Few Things About Division by Zero in C
Getting Actual File Name (With Proper Casing) on Windows
How to Convert a Tchar Array to Std::String
Is It Good Practice to Make Member Variables Protected
Why Double Can Store Bigger Numbers Than Unsigned Long Long
Observer Design Pattern in C++
Running a Windows Program and Detect When It Ends with C++
How to Invoke Pointer to Member Function When It's a Class Data Member
A Better Way to Split a String into an Array of Strings in C/C++ Using Whitespace as a Delimiter
What Is a C++11 Extension [-Wc++11-Extensions]
In C++ How Is Function Overloading Typically Implemented
Stopping an Infinite Loop in C++ When Key Is Pressed
How to Turn on Multi-Cpu/Core C++ Compiles in the Visual Studio Ide (2008)
Is Static Object Guaranteed to Be Initialized
Fseek Does Not Work When File Is Opened in "A" (Append) Mode