Si paso el siguiente código a través de mi instantánea de GCC 4.7, intenta copiar los unique_ptr
s en el vector.
#include <vector>
#include <memory>
int main() {
using move_only = std::unique_ptr<int>;
std::vector<move_only> v { move_only(), move_only(), move_only() };
}
Obviamente, eso no puede funcionar porque std::unique_ptr
no se puede copiar:
error: uso de la función eliminada 'std :: unique_ptr <_Tp, _Dp> :: unique_ptr (const std :: unique_ptr <_Tp, _Dp> &) [with _Tp = int; _Dp = std :: default_delete; std :: unique_ptr <_Tp, _Dp> = std :: unique_ptr] '
¿GCC tiene razón al intentar copiar los punteros de la lista de inicializadores?
c++
c++11
initializer-list
move-semantics
R. Martinho Fernandes
fuente
fuente
Respuestas:
La sinopsis de
<initializer_list>
en 18.9 deja razonablemente claro que los elementos de una lista de inicializadores siempre se pasan a través de const-reference. Desafortunadamente, no parece haber ninguna forma de usar move-semantic en los elementos de la lista de inicializadores en la revisión actual del lenguaje.Específicamente, tenemos:
fuente
const
, que no pueden descartarse en un programa bien formado.Editar: dado que @Johannes no parece querer publicar la mejor solución como respuesta, simplemente lo haré.
Los iteradores devueltos por
std::make_move_iterator
moverán el elemento apuntado cuando se desreferencia.Respuesta original: vamos a utilizar un pequeño tipo de ayuda aquí:
Lamentablemente, el código sencillo aquí no funcionará:
Dado que el estándar, por la razón que sea, no define un constructor de copia de conversión como este:
El
initializer_list<rref_wrapper<move_only>>
creado por brace-init-list ({...}
) no se convertirá alinitializer_list<move_only>
quevector<move_only>
toma. Entonces, necesitamos una inicialización de dos pasos aquí:fuente
std::ref
, ¿no? Quizás debería llamarsestd::rref
.move_only m[] = { move_only(), move_only(), move_only() }; std::vector<move_only> v(std::make_move_iterator(m), std::make_move_iterator(m + 3));
.move_iterator
mensajes todavía.std::distance
para iteradores de avance o mejor ystd::move_iterator
adapta la categoría del iterador subyacente. De todos modos, buena y concisa solución. Publicarlo como respuesta, ¿quizás?Como se mencionó en otras respuestas, el comportamiento de
std::initializer_list
es sostener objetos por valor y no permitir que se muevan, por lo que esto no es posible. Aquí hay una posible solución, usando una llamada de función donde los inicializadores se dan como argumentos variadic:Desafortunadamente,
multi_emplace(foos, {});
falla ya que no puede deducir el tipo{}
, por lo que para que los objetos se construyan por defecto, debe repetir el nombre de la clase. (o usarvector::resize
)fuente
Usando el truco de Johannes Schaub de
std::make_move_iterator()
constd::experimental::make_array()
, puede usar una función auxiliar:Véalo en vivo Coliru.
Quizás alguien pueda aprovechar
std::make_array()
los trucos para permitirmake_vector()
hacer lo suyo directamente, pero no vi cómo (más exactamente, intenté lo que pensé que debería funcionar, fallé y seguí adelante). En cualquier caso, el compilador debería poder integrar la matriz a la transformación vectorial, como lo hace Clang con O2 activado GodBolt.fuente
Como se ha señalado, no es posible inicializar un vector de tipo de solo movimiento con una lista de inicializadores. La solución propuesta originalmente por @Johannes funciona bien, pero tengo otra idea ... ¿Qué pasa si no creamos una matriz temporal y luego movemos elementos desde allí al vector, pero usamos la ubicación
new
para inicializar esta matriz ya en lugar de la bloque de memoria del vector?Aquí está mi función para inicializar un vector de
unique_ptr
usando un paquete de argumentos:fuente
result.data()
no es un puntero a una memoria aleatoria. Es un puntero a un objeto . Piense en lo que le sucede a ese pobre objeto cuando coloca uno nuevo sobre él.