2012-09-15 7 views
20

ho provato a compilare i seguenti frammenti con gcc4.7tuple vettoriale e 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'}}; 

Tuttavia, per il vettore di tuple, il compilatore si lamenta:

errore: la conversione a 'std :: tuple 'dalla lista di inizializzazione userebbe il costruttore esplicito' constexpr std :: tupla <> :: tupla (_UElements & & ...) [con _UElements = {int, double, char}; = vuoto; _Elements = {int, double, char}] '

Le informazioni sull'errore riversate dal compilatore sono per me del tutto incomprensibili e non ho idea di come siano stati implementati i costruttori della tupla, ma so che sono completamente a posto con l'inizializzazione uniforme (come: tuple<int,float,char>{1,2.2,'X'}), quindi, mi chiedo se il problema che ho riscontrato sia solo un TODO del compilatore o qualcosa definito dallo standard C++ 11.

Qualsiasi risposta sarà apprezzata!

risposta

25

Se si controlla un reference page sul costruttore tuple, vedrete che non ci vuole std::initializer_list che è necessario per la classe di essere in grado di accettare le liste di inizializzazione.

si inizializza il vettore 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

Scusate! Ho fatto un refuso, riadattato, ma ancora non funzionerà! – Need4Steed

+0

@ Need4Steed Aggiornato la mia risposta. –

+0

Ho provato make_tuple, funziona bene, ma mi sembra incoerente se va bene con le coppie ma non con le tuple. – Need4Steed

5

Le rilevanti std::tuple costruttori sono explicit. Ciò significa che ciò che si vuole fare non è possibile, poiché la sintassi che si desidera utilizzare è definita in termini di inizializzazione della copia (che vieta di chiamare un costruttore explicit). Al contrario, std::tuple<int, float, char> { 1, 2.2, 'X' } utilizza l'inizializzazione diretta. std::pair ha solo costruttori non explicit.

Utilizzare l'inizializzazione diretta o una delle funzioni di fabbrica standard della tupla (ad esempio std::make_tuple).

0

Questo è effettivamente possibile, con le caratteristiche di C++ 11.

Sì, l'inizializzatore_list desidera che tutti gli elementi siano dello stesso tipo. Il trucco è che possiamo creare una classe wrapper che può essere static_cast per tutti i tipi che vogliamo. Questo è facile da raggiungere:

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 i costruttori conversione implicita, possiamo passare qualcosa come {1,2.5, 'c', 4} a un initializer_list (o un vettore, che converte implicitamente l'initializer_list) di tipo MultiTypeWrapper. Ciò significa che non possiamo scrivere una funzione come sotto ad accettare tale intializer_list come argomento:

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

Usiamo un altro trucco per lanciare ogni valore nel vettore al suo tipo originale (notare che forniamo conversione implicita in la definizione di MultiTypeWrapper) e assegnarla allo slot corrispondente in una tupla. E 'come una ricorsione sugli argomenti di template:

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; 
} 

Nota che dobbiamo creare la classe di supporto per set_tuple dal C++ non supporta la funzione di specializzazione.Ora, se vogliamo testare il codice:

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()); 

Il risultato sarebbe:

1 2.50 ABC 

Questo è testato sul mio desktop con clangore 3.2

Speranza aiuta il mio ingresso :)