I noticed that aggregate list initalization of std::vector performs copy initialization when move is more applicable. At the same time, multiple emplace_backs do what I want.
I could only come up with this imperfect solution of writing a template function init_emplace_vector
. It's only optimal for non-explicit single-value constructors, though.
template <typename T, typename... Args>
std::vector<T> init_emplace_vector(Args&&... args)
{
std::vector<T> vec;
vec.reserve(sizeof...(Args)); // by suggestion from user: eerorika
(vec.emplace_back(std::forward<Args>(args)), ...); // C++17
return vec;
}
Do I really need to use emplace_back in order to initialize std::vector as efficiently as possible?
// an integer passed to large is actually the size of the resource
std::vector<large> v_init {
1000, // instance of class "large" is copied
1001, // copied
1002, // copied
};
std::vector<large> v_emplaced;
v_emplaced.emplace_back(1000); // moved
v_emplaced.emplace_back(1001); // moved
v_emplaced.emplace_back(1002); // moved
std::vector<large> v_init_emplace = init_emplace_vector<large>(
1000, // moved
1001, // moved
1002 // moved
);
Class large
produces information about copies/moves (implementation below) and so the output of my program is:
- initializer
large copy
large copy
large copy
- emplace_back
large move
large move
large move
- init_emplace_vector
large move
large move
large move
My implementation of large
is simply a copyable/movable type holding a large resource that warns on copy/move.
struct large
{
large(std::size_t size) : size(size), data(new int[size]) {}
large(const large& rhs) : size(rhs.size), data(new int[rhs.size])
{
std::copy(rhs.data, rhs.data + rhs.size, data);
std::puts("large copy");
}
large(large&& rhs) noexcept : size(rhs.size), data(rhs.data)
{
rhs.size = 0;
rhs.data = nullptr;
std::puts("large move");
}
large& operator=(large rhs) noexcept
{
std::swap(*this, rhs);
return *this;
}
~large() { delete[] data; }
int* data;
std::size_t size;
};
By using reserve, there is no copy or move. Only large::large(std::size_t)
constructor is invoked. True emplace.
Begin Declare v of vector type. Call push_back() function to insert values into vector v. Print “Vector elements:”. for (int a : v) print all the elements of variable a.
std::initializer_list This type is used to access the values in a C++ initialization list, which is a list of elements of type const T .
A std::vector can be initialized in several ways while declaring it: A vector can be initialized from another container in several ways: Copy construction (from another vector only), which copies data from v2:
Following are different ways to create and initialize a vector in C++ STL. Initializing by one by one pushing values : // CPP program to create an empty vector. // and one by one push values. #include <bits/stdc++.h>. using namespace std; int main()
Iterator move-construction, using std::make_move_iterator, which moves elements into v: With the help of the assign () member function, a std::vector can be reinitialized after its construction:
Today, we solve " ... a herefore unsolved problem in C++" (Bjarne Stroustrup). To make the long story short, I will write about perfect forwarding. But, what is perfect forwarding? If a function templates forward its arguments without changing its lvalue or rvalue characteristics, we call it perfect forwarding. Great.
Can I aggregate-initialize std::vector ...
No. std::vector
is not an aggregate, so it cannot be aggregate-initialised.
You may mean list-initialisation instead, in which case:
Can I [list-initialise] std::vector with perfect forwarding of the elements?
No. List-initialisation uses the std::initializer_list
constructor and std::initializer_list
copies its arguments.
Your init_emplace_vector
appears to be a decent solution, although it can be improved by reserving the memory before emplacing the elements.
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