Convertir un vector a una lista inicializadora


Todo el mundo crea std::vector a partir de std::initializer_list, pero ¿qué pasa al revés?

Eg. si utiliza un std::initializer_list como parámetro:

void someThing(std::initializer_list<int> items)
{
...
}

Hay momentos en que usted tiene sus elementos en una vector<T> en lugar de una lista literal:

std::vector<int> v;
// populate v with values
someThing(v); // boom! No viable conversion etc.

La pregunta más general es: cómo crear un stl::initializer_list a partir de un iterable STL, no solo std::vector.

Author: TemplateRex, 2013-09-19

6 answers

Publiqué una forma que parecía funcionar, pero desafortunadamente causó violaciones de acceso a la memoria debido a cómo las listas de inicialización se tratan como referencias a copias de valores de ámbito local.

Aquí hay una alternativa. Se genera una función y una lista de inicializadores estáticos independientes para cada número posible de elementos, que se cuentan con un paquete de parámetros. Esto no es seguro para subprocesos y usa un const_cast (que se considera muy malo) para escribir en la memoria static initializer_list. Sin embargo, funciona limpiamente tanto en gcc como en clang.

Si por alguna razón oscura necesita este problema resuelto y no tiene otras opciones, puede probar este truco.

#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <type_traits>
#include <vector>

namespace __range_to_initializer_list {

    constexpr size_t DEFAULT_MAX_LENGTH = 128;

    template <typename V> struct backingValue { static V value; };
    template <typename V> V backingValue<V>::value;

    template <typename V, typename... Vcount> struct backingList { static std::initializer_list<V> list; };
    template <typename V, typename... Vcount>
    std::initializer_list<V> backingList<V, Vcount...>::list = {(Vcount)backingValue<V>::value...};

    template <size_t maxLength, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) >= maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        throw std::length_error("More than maxLength elements in range.");
    }

    template <size_t maxLength = DEFAULT_MAX_LENGTH, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) < maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        if (current != end)
            return generate_n<maxLength, It, V, V, Vcount...>(begin, end, ++current);

        current = begin;
        for (auto it = backingList<V,Vcount...>::list.begin();
             it != backingList<V,Vcount...>::list.end();
             ++current, ++it)
            *const_cast<V*>(&*it) = *current;

        return backingList<V,Vcount...>::list;
    }

}

template <typename It>
std::initializer_list<typename It::value_type> range_to_initializer_list(It begin, It end)
{
    return __range_to_initializer_list::generate_n(begin, end, begin);
}

int main()
{
    std::vector<int> vec = {1,2,3,4,5,6,7,8,9,10};
    std::initializer_list<int> list = range_to_initializer_list(vec.begin(), vec.end());
    for (int i : list)
        std::cout << i << std::endl;
    return 0;
}
 0
Author: fuzzyTew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-04 04:22:18

La respuesta es NO, no puedes hacer eso.

Un objeto de tipo std::initializer_list<T> es un objeto proxy ligero que proporciona acceso a una matriz de objetos de tipo T. A std::initializer_list el objeto es construido automáticamente cuando:

  • una lista de inicio entre corchetes se usa en la inicialización de listas, incluyendo la inicialización de listas de llamadas a funciones y expresiones de asignación (no debe confundirse con las listas inicializadoras de constructores)
  • una lista de inicio entre corchetes está enlazada a auto, incluyendo a distancia para bucle

En cuanto al soporte de bibliotecas, std::initializer_list solo tiene un constructor predeterminado que construye una lista vacía, y sus iteradores son constantes. La falta de un miembro push_back() significa que no puede aplicar, por ejemplo, un std::copy con un adaptador de iterador std::back_inserter para llenarlo, y tampoco puede asignar a través de dichos iteradores directamente:

#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <vector>

int main() 
{
    auto v = std::vector<int> { 1, 2 };
    std::initializer_list<int> i;
    auto it = std::begin(i);
    *it = begin(v); // error: read-only variable is not assignable
}

Ejemplo En Vivo

Si nos fijamos en los Contenedores estándar, además de aceptar std::initializer_list en su constructores / insertadores, todos ellos tienen constructores / insertadores tomando un par iterador, y es probable que la implementación delegue la función initializer_list a la función de par iterador correspondiente. Por ejemplo, el std::vector<T>::insert la función en libc++ es esta simple línea:

 iterator insert(const_iterator __position, initializer_list<value_type> __il)
        {return insert(__position, __il.begin(), __il.end());}

Usted debe modificar su código en líneas similares:

void someThing(std::initializer_list<int> items)
{
    someThing(items.begin(), items.end()); // delegate
}

template<class It>
void someThing(It first, It last)
{
    for (auto it = first, it != last; ++it) // do your thing
}

En los momentos en que tiene sus elementos en un vector en lugar de una lista literal:

std::vector<int> v = { 1, 2 };
auto i = { 1, 2 };
someThing(begin(v), end(v)); // OK
someThing(i); // also OK
someThing({1, 2}); // even better
 24
Author: TemplateRex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-16 17:12:53

Aparentemente no, no es posible. No existe tal constructor (y creo que por buenas razones), std::initializer_list es una criatura extraña.

Lo que podría hacer en su lugar es cambiar someThing() para aceptar un par de iteradores. De esa manera obtienes lo que quieres, siempre que puedas cambiar la firma de esa función (no está en una biblioteca de terceros, etc.).

 6
Author: Ali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-19 14:04:21

Sí puedes hacer esto, pero no quieres hacerlo, porque cómo tienes que hacerlo es bastante tonto.

Primero, determine cuál es la longitud máxima de su lista. Debe haber una longitud máxima, porque size_t no es ilimitada. Lo ideal es encontrar uno mejor (más pequeño), como 10.

En segundo lugar, escriba el código magic switch que toma un entero en tiempo de ejecución, y lo asigna a un entero en tiempo de compilación, y luego invoca una clase o función de plantilla con ese entero en tiempo de compilación. Tal código necesita un número entero máximo tamaño use usa la longitud máxima arriba.

Ahora, magic cambia el tamaño del vector a una longitud de tiempo de compilación.

Crea una secuencia de tiempo de compilación de enteros, desde 0 hasta length-1. Desempaquetar esa secuencia en una construcción initializer_list, invocando cada vez [] en el std::vector. Llame a su función con el resultado initializer_list.

Lo anterior es complicado y ridículo y la mayoría de los compiladores explotarán en él. Hay un paso que no estoy seguro de la legalidad de -- es la construcción de un initializer_list a ¿un lugar legal para desempacar el argumento varardic?

Aquí hay un ejemplo de un interruptor mágico: ¿Puedo separar las ubicaciones de creación y uso de las estrategias en tiempo de compilación?

Aquí hay un ejemplo de los índices, o secuencia, truco: Argumentos Constructor de tupla

Este post solo debería ser de interés teórico, porque prácticamente esta es una forma realmente tonta de resolver este problema.

Hacerlo con un iterable arbitrario es más difícil, sin hacer n^2 trabajo. Pero como lo anterior ya es suficientemente ridículo, y la versión iterable arbitraria sería más ridícula... (Tal vez con un paquete de lambdas getting conseguir que los argumentos se evalúan en orden podría ser complicado. ¿Hay un punto de secuencia entre la evaluación de los diversos argumentos a una lista inicializadora?)

 5
Author: Yakk - Adam Nevraumont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:00:34

Para un vector se puede usar aritmética de puntero.

Foo( std::initializer_list< _Type >( aVector.data(), aVector.data() + aVector.size() ) ) ;

Podría envolver esto en una función genérica.

template< typename _Type >
auto    InitList( const _Type * begin, size_t size )
{
  return std::initializer_list< _Type >( begin, begin + size ) ;
}

Llame a la función de esta manera.

Foo( InitList( aVector.data(), aVector.size() ) ) ;
 1
Author: QBziZ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-28 09:00:11

Si no te molestan las copias, entonces creo que algo como esto funcionaría:

template<class Iterator>
using iterator_init_list = std::initializer_list<typename std::iterator_traits<Iterator>::value_type>;

template<class Iterator, class... Ts>
iterator_init_list<Iterator> to_initializer_list(Iterator start, Iterator last, Ts... xs)
{
    if (start == last) return iterator_init_list<Iterator>{xs...};
    else return to_initializer_list(start+1, last, xs..., *start);
}
 0
Author: Paul Fultz II,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-03 06:10:07