vectores una programacion matriz matrices llenar ingresar imprimir funciones ejemplos datos con como clase c++ c++11 initializer-list move-semantics

c++ - una - vectores en c



¿Puedo listar-inicializar un vector de tipo mover solo? (5)

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 de allí al vector, pero usamos la ubicación new para inicializar esta matriz que ya está en el lugar de el bloque de memoria del vector?

Aquí está mi función para inicializar un vector de unique_ptr usando un paquete de argumentos:

#include <iostream> #include <vector> #include <make_unique.h> /// @see http://stackoverflow.com/questions/7038357/make-unique-and-perfect-forwarding template <typename T, typename... Items> inline std::vector<std::unique_ptr<T>> make_vector_of_unique(Items&&... items) { typedef std::unique_ptr<T> value_type; // Allocate memory for all items std::vector<value_type> result(sizeof...(Items)); // Initialize the array in place of allocated memory new (result.data()) value_type[sizeof...(Items)] { make_unique<typename std::remove_reference<Items>::type>(std::forward<Items>(items))... }; return result; } int main(int, char**) { auto testVector = make_vector_of_unique<int>(1,2,3); for (auto const &item : testVector) { std::cout << *item << std::endl; } }

Si paso el siguiente código a través de mi instantánea GCC 4.7, intenta copiar el 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> &) [con _Tp = int; _Dp = std :: default_delete; std :: unique_ptr <_Tp, _Dp> = std :: unique_ptr] ''

¿GCC está correcto al intentar copiar los punteros de la lista de inicializadores?


Como se menciona en otras respuestas, el comportamiento de std::initializer_list es mantener los 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 a función donde los inicializadores se dan como argumentos variados:

#include <vector> #include <memory> struct Foo { std::unique_ptr<int> u; int x; Foo(int x = 0): x(x) {} }; template<typename V> // recursion-ender void multi_emplace(std::vector<V> &vec) {} template<typename V, typename T1, typename... Types> void multi_emplace(std::vector<V> &vec, T1&& t1, Types&&... args) { vec.emplace_back( std::move(t1) ); multi_emplace(vec, args...); } int main() { std::vector<Foo> foos; multi_emplace(foos, 1, 2, 3, 4, 5); multi_emplace(foos, Foo{}, Foo{}); }

Lamentablemente, multi_emplace(foos, {}); falla ya que no puede deducir el tipo para {} , por lo que para que los objetos se construyan por defecto, debe repetir el nombre de la clase. (o use vector::resize )


La sinopsis de <initializer_list> en 18.9 deja bastante 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-semántica en los elementos de la lista de inicializadores en la revisión actual del idioma.

Específicamente, tenemos:

typedef const E& reference; typedef const E& const_reference; typedef const E* iterator; typedef const E* const_iterator; const E* begin() const noexcept; // first element const E* end() const noexcept; // one past the last element


Usando el truco de Johannes Schaub de std::make_move_iterator() con std::experimental::make_array() , puedes usar una función auxiliar:

#include <memory> #include <type_traits> #include <vector> #include <experimental/array> struct X {}; template<class T, std::size_t N> auto make_vector( std::array<T,N>&& a ) -> std::vector<T> { return { std::make_move_iterator(std::begin(a)), std::make_move_iterator(std::end(a)) }; } template<class... T> auto make_vector( T&& ... t ) -> std::vector<typename std::common_type<T...>::type> { return make_vector( std::experimental::make_array( std::forward<T>(t)... ) ); } int main() { using UX = std::unique_ptr<X>; const auto a = std::experimental::make_array( UX{}, UX{}, UX{} ); // Ok const auto v0 = make_vector( UX{}, UX{}, UX{} ); // Ok //const auto v1 = std::vector< UX >{ UX{}, UX{}, UX{} }; // !! Error !! }

Coliru en vivo en Coliru .

Quizás alguien pueda aprovechar el std::make_array() para permitir que make_vector() haga su trabajo directamente, pero no vi cómo (más exactamente, intenté lo que pensé que debería funcionar, falló y se movió). En cualquier caso, el compilador debería poder alinear la matriz con la transformación vectorial, ya que Clang lo hace con O2 en GodBolt .


Editar: Ya que @Johannes no parece querer publicar la mejor solución como respuesta, simplemente lo haré.

#include <iterator> #include <vector> #include <memory> int main(){ using move_only = std::unique_ptr<int>; move_only init[] = { move_only(), move_only(), move_only() }; std::vector<move_only> v{std::make_move_iterator(std::begin(init)), std::make_move_iterator(std::end(init))}; }

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í:

#include <utility> #include <type_traits> template<class T> struct rref_wrapper { // CAUTION - very volatile, use with care explicit rref_wrapper(T&& v) : _val(std::move(v)) {} explicit operator T() const{ return T{ std::move(_val) }; } private: T&& _val; }; // only usable on temporaries template<class T> typename std::enable_if< !std::is_lvalue_reference<T>::value, rref_wrapper<T> >::type rref(T&& v){ return rref_wrapper<T>(std::move(v)); } // lvalue reference can go away template<class T> void rref(T&) = delete;

Lamentablemente, el código directo aquí no funcionará:

std::vector<move_only> v{ rref(move_only()), rref(move_only()), rref(move_only()) };

Dado que el estándar, por la razón que sea, no define un convertidor de copiado como este:

// in class initializer_list template<class U> initializer_list(initializer_list<U> const& other);

El initializer_list<rref_wrapper<move_only>> creado por el brace-init-list ( {...} ) no se convertirá en la initializer_list<move_only> que toma el vector<move_only> . Entonces, necesitamos una inicialización de dos pasos aquí:

std::initializer_list<rref_wrapper<move_only>> il{ rref(move_only()), rref(move_only()), rref(move_only()) }; std::vector<move_only> v(il.begin(), il.end());