2015-02-15 43 views
8

E 'da un po' che sto facendo C++ ma non ho familiarità con i modelli.Iterating on a tuple ... again

Recentemente, ho provato a scrivere una classe che avvolge un std::vector<std::tuple<Types...>>. Questa classe deve avere funzioni membro e ho davvero bisogno di essere in grado di iterare sopra la tupla. Infatti, se sono in grado di stampare ogni elemento di una tupla (nell'ordine), sarei in grado di fare tutto ciò di cui ho bisogno.

Ho trovato una soluzione utilizzando un cast, ma non ne sono molto sicuro dato che si basa su un cast che non mi piace molto (inoltre, quando provo a usare static_cast, non viene compilato più).

La mia domanda è, è il seguente codice corretto, portatile, è un hack e dovrei trovare un altro modo per farlo rispetto a utilizzare questo cast? Inoltre, questo cast è probabilmente un cast runtime giusto? C'è un modo per fare ciò che voglio senza questo?

std::ostream& operator<<(std::ostream& out, std::tuple<> const& tuple) 
{ 
    return out; // Nothing to do here 
} 

template<typename First, typename... Types> 
std::ostream& operator<<(std::ostream& out, std::tuple<First, Types...> const& tuple) 
{ 
    out << std::get<0>(tuple) << " "; 

    // The cast that I don't like 
    return out << (std::tuple<Types...>&) tuple; 
} 

int main() 
{ 
    auto tuple = std::make_tuple(1, 2.3, "Hello"); 
    std::cout << tuple << std::endl; 
    return 0; 
} 

Grazie in anticipo per le vostre risposte.

+0

Quella trasmissione sembra un 'reinterpret_cast'. Quindi no, non va bene. – 0x499602D2

+1

Questo anche [produce un segfault] (http://coliru.stacked-crooked.com/a/9f96f1e786b070f1) quando si usa clang ++ & libC++ – dyp

+0

@dyp Questo è esattamente quello che volevo sapere, grazie! – tforgione

risposta

10

Utilizzare std::index_sequence_for per divertimento e profitto.

template <typename TupleLike, size_t ... Inds> 
std::ostream& PrintHelper(std::ostream& out, TupleLike const& tuple, std::index_sequence<Inds...>) 
{ 
    int unused[] = {0, (void(out << std::get<Inds>(tuple) << " "), 0)...}; 
    (void)unused; 
    return out; 
} 

template<typename... Types> 
std::ostream& operator<<(std::ostream& out, std::tuple<Types...> const& tuple) 
{ 
    return PrintHelper(out, tuple, std::index_sequence_for<Types...>()); 
} 

MODIFICA: Live Demo. Grazie a @dyp. Questo utilizza un espediente di espansione da this answer.

+0

Nota che ciò causa il danneggiamento dello stack su MSVC, ma l'equivalente usando 'std :: array <>' funziona correttamente. – ildjarn

+1

VC++ 2015 ha risolto il problema di corruzione dello stack. – ildjarn

0

Ho trovato un altro modo per fare ciò che voglio. Ho usato this article che può stampare gli elementi di una tupla in ordine decrescente, e io uso un secondo indice J == std::tuple_size<std::tuple<Types...>>::value - I così posso specializzare il modello quando I==0.

template<std::size_t I, std::size_t J, typename... Types> 
struct printHelper 
{ 
    std::ostream& operator()(std::ostream& out, std::tuple<Types...> const& tuple) 
    { 
     out << std::get<J>(tuple) << " "; 

     // Recursive call without cast 
     return printHelper<I-1,J+1,Types...>{}(out, tuple); 
    }; 
}; 

// Specialization on the last element when I==0 
template<std::size_t J, typename... Types> 
struct printHelper<0,J,Types...> 
{ 
    std::ostream& operator()(std::ostream& out, std::tuple<Types...> const& tuple) 
    { 
     // Nothing to do here 
     return out; 
    } 
}; 

template<typename... Types> 
std::ostream& operator<<(std::ostream& out, std::tuple<Types...> const& tuple) 
{ 
    return printHelper<std::tuple_size<std::tuple<Types...>>::value, 0, Types...>{}(out, tuple); 
}