Noté que la inicialización de la lista agregada de std :: vector realiza la inicialización de copia cuando el movimiento es más aplicable. Al mismo tiempo, múltiples emplace_backs hacen lo que quiero.
Solo pude encontrar esta solución imperfecta de escribir una función de plantilla init_emplace_vector
. Sin embargo, solo es óptimo para constructores de valor único no explícitos .
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;
}
Pregunta
¿Realmente necesito usar emplace_back para inicializar std :: vector de la manera más eficiente posible?
// 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
);
Salida
La clase large
produce información sobre copias / movimientos (implementación a continuación) y, por lo tanto, el resultado de mi programa es:
- initializer
large copy
large copy
large copy
- emplace_back
large move
large move
large move
- init_emplace_vector
large move
large move
large move
Implementación de clase grande
Mi implementación de large
es simplemente un tipo copiable / móvil que contiene un gran recurso que advierte sobre la copia / movimiento.
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;
};
Editar
Al usar la reserva, no hay copia ni movimiento. Solo large::large(std::size_t)
se invoca el constructor. El verdadero lugar.
fuente
std::initializer_list
.std::array
.operator=
que destruye la fuente es bastante inusual y puede causar problemas inesperados.init_emplace_vector
podría mejorarse convec.reserve(sizeof...(Args))
operator=
no destruye la fuente. Es el idioma de intercambio de copias.Respuestas:
No.
std::vector
no es un agregado, por lo que no se puede inicializar de forma agregada.Puede referirse a la inicialización de la lista, en cuyo caso:
No. List-initialialisation usa el
std::initializer_list
constructor ystd::initializer_list
copia sus argumentos.Su
init_emplace_vector
parece ser una solución decente, aunque se puede mejorar reservando la memoria antes de emplazar los elementos.fuente