c++ - Tupla vector y initializer_list
c++11 tuples (2)
Esto es realmente factible, con características de c ++ 11.
Sí, el initializer_list quiere 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 queramos. 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ícitos, podemos pasar algo como {1,2.5, ''c'', 4} a una lista_inicializadora (o vector, que convierte implícitamente la lista_inicializadora) de tipo MultiTypeWrapper. Esto significa que no podemos escribir una función como la siguiente para aceptar el intializer_list como argumento:
template <typename... T>
std::tuple<T...> create_tuple(std::vector<unit_test::MultiTypeWrapper<T...> > init) {
....
}
Usamos otro truco para convertir cada valor en el vector a su tipo original (tenga en cuenta que proporcionamos una conversión implícita en la definición de MultiTypeWrapper
) y lo asignamos a la ranura correspondiente en una tupla. Es como una recursión en los argumentos de la 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 auxiliar para set_tuple
ya que c ++ no admite la especialización de funciones. Ahora 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 se prueba en mi escritorio con Clang 3.2
Espero que mi entrada ayude :)
Intenté compilar los siguientes fragmentos con gcc4.7
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 :: tuple'' desde la lista de inicializadores usaría el constructor explícito ''constexpr std :: tuple <> :: tuple (_UElements && ...) [con _UElements = {int, double, char}; = vacío; _Elementos = {int, double, char}] ''
La información de error que el compilador ha derramado es una tontería total para mí, y no tengo idea de cómo se implementaron los constructores de tuple, pero sí 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 una DEMANDA del compilador o es algo definido por el estándar C ++ 11.
Cualquier respuesta será apreciada!
Los constructores std::tuple
relevantes son explicit
. Esto significa que lo que quiere hacer no es posible, ya que la sintaxis que desea usar está definida en términos de inicialización de copia (lo que prohíbe llamar a un constructor explicit
). En contraste, std::tuple<int, float, char> { 1, 2.2, ''X'' }
usa la inicialización directa. std::pair
tiene constructores no explicit
solamente.
Utilice la inicialización directa o una de las funciones de fábrica de tuplas estándar (por ejemplo, std::make_tuple
).