2012-09-15 18 views
20

yo tratamos de recopilar los siguientes fragmentos con gcc4.7vector de tupla y initializer_list

vector<pair<int,char> > vp = {{1,'a'},{2,'b'}}; 
//For pair vector, it works like a charm. 

vector<tuple<int,double,char> > vt = {{1,0.1,'a'},{2,4.2,'b'}}; 

Sin embargo, para el vector de tuplas, el compilador se queja:

error: convertir a 'std :: tupla 'desde la lista de inicializadores usaría el constructor explícito' constexpr std :: tuple <> :: tuple (_Elementos & & ...) [con _UElements = {int, double, char}; = vacío; _Elements = {int, double, char}] '

La información de error derramada por el compilador es un verdadero galimatías para mí, y no tengo idea de cómo se implementaron los constructores de tuple, pero sé que están totalmente de acuerdo con una inicialización uniforme (como: tuple<int,float,char>{1,2.2,'X'}), por lo tanto, me pregunto si el problema que encontré es solo un TODO del compilador o algo definido por el estándar C++ 11.

¡Cualquier respuesta será apreciada!

Respuesta

25

Si marca un reference page en el tuple constructor, se verá que no hace falta ser std::initializer_list que se necesita para que la clase sea capaz de aceptar las listas de inicializador.

inicializar el vector con std::make_tuple:

vector<tuple<int,double,char>> vt = 
    { std::make_tuple(1,0.1,'a'), std::make_tuple(2,4.2,'b') }; 
+0

¡Lo siento! Hice un error tipográfico, reeditado, pero todavía no funciona! – Need4Steed

+0

@ Need4Steed Actualizado mi respuesta. –

+0

Probé make_tuple, funciona bien, pero parece un poco inconsistente para mí si está bien con las parejas pero no con las tuplas. – Need4Steed

5

Los constructores std::tuple relevantes son explicit. Esto significa que lo que desea hacer no es posible, ya que la sintaxis que desea utilizar se define en términos de inicialización de copia (que prohíbe llamar a un constructor explicit). Por el contrario, std::tuple<int, float, char> { 1, 2.2, 'X' } utiliza la inicialización directa. std::pair tiene constructores que no son explicit solamente.

O utilice la inicialización directa o una de la función de fábrica de tupla estándar (por ejemplo, std::make_tuple).

0

Esto es realmente factible, con características de C++ 11.

Sí, la initializer_list desea que todos sus elementos sean del mismo tipo. El truco es que podemos crear una clase contenedora que puede ser static_cast para todos los tipos que queremos. Esto es fácil de lograr:

template <typename... tlist> 
class MultiTypeWrapper { 
}; 

template <typename H> 
class MultiTypeWrapper<H> { 
public: 
    MultiTypeWrapper() {} 

    MultiTypeWrapper(const H &value) : value_(value) {} 

    operator H() const { 
    return value_; 
    } 
private: 
    H value_; 
}; 

template <typename H, typename... T> 
class MultiTypeWrapper<H, T...> 
    : public MultiTypeWrapper<T...> { 

public: 
    MultiTypeWrapper() {} 

    MultiTypeWrapper(const H &value) : value_(value) {} 

    // If the current constructor does not match the type, pass to its ancestor. 
    template <typename C> 
    MultiTypeWrapper(const C &value) : MultiTypeWrapper<T...>(value) {} 

    operator H() const { 
    return value_; 
    } 
private: 
    H value_; 
}; 

Con los constructores de conversión implícitas, podemos pasar algo como {1,2.5, 'c', 4} para un initializer_list (o vector, que convierte implícitamente la initializer_list) de tipo MultiTypeWrapper. Esto significa que no podemos escribir una función como continuación a aceptar tales intializer_list como argumento:

template <typename... T> 
std::tuple<T...> create_tuple(std::vector<unit_test::MultiTypeWrapper<T...> > init) { 
    .... 
} 

Utilizamos otro truco para emitir cada valor en el vector de su tipo original (nota que proporcionamos conversión implícita en la definición de MultiTypeWrapper) y asignarlo a la ranura correspondiente en una tupla. Es como una recursividad en argumentos de plantilla:

template <int ind, typename... T> 
class helper { 
public: 
    static void set_tuple(std::tuple<T...> &t, const std::vector<MultiTypeWrapper<T...> >& v) { 
    std::get<ind>(t) = static_cast<typename std::tuple_element<ind,std::tuple<T...> >::type>(v[ind]); 
    helper<(ind-1),T...>::set_tuple(t,v); 
    } 
}; 



template <typename... T> 
class helper<0, T...> { 
public: 
    static void set_tuple(std::tuple<T...> &t, const std::vector<MultiTypeWrapper<T...> >& v) { 
    std::get<0>(t) = static_cast<typename std::tuple_element<0,std::tuple<T...> >::type>(v[0]); 
    } 
}; 



template <typename... T> 
std::tuple<T...> create_tuple(std::vector<unit_test::MultiTypeWrapper<T...> > init) { 
    std::tuple<T...> res; 
    helper<sizeof...(T)-1, T...>::set_tuple(res, init); 
    return res; 
} 

Tenga en cuenta que tenemos que crear la clase de ayuda para set_tuple desde C++ no es compatible con la especialización de funciones.Ahora bien, si queremos probar el código:

auto t = create_tuple<int,double,std::string>({1,2.5,std::string("ABC")}); 
printf("%d %.2lf %s\n", std::get<0>(t), std::get<1>(t), std::get<2>(t).c_str()); 

La salida sería:

1 2.50 ABC 

Esto es probado en mi escritorio con sonido metálico de 3,2

espero que mi aportación ayuda :)