2013-02-15 24 views
6

Załóżmy, że mam szablon, który jest parametryzowany przez typ klasy i liczbę typów argumentów. zestaw argumentów pasujących do tych typów jest przechowywany w krotce. Jak można je przekazać do konstruktora klasy?Argumenty konstruktora z krotki

Niemal w C++ 11 kod:

template<typename T, typename... Args> 
struct foo { 
    tuple<Args...> args; 
    T gen() { return T(get<0>(args), get<1>(args), ...); } 
}; 

jaki sposób ... w wywołaniu konstruktora być wypełnione bez ustalania długości?

Chyba mógłbym wymyślić jakiś skomplikowany mechanizm rekursywnych wywołań szablonów, który to robi, ale nie mogę uwierzyć, że jestem pierwszym, który tego chce, więc domyślam się, że będą gotowe rozwiązania do tego, być może nawet w standardowych bibliotekach.

Odpowiedz

5

Potrzebujesz szablonu maszyny meta-programowania do osiągnięcia tego.

Najprostszym sposobem na wywołanie argumentu jest wykorzystanie rozszerzenia paczek dla wyrażeń, które zawierają spakowaną sekwencję liczb całkowitych z liczb całkowitych. Aby zbudować taką sekwencję, niezbędna jest maszyna szablonowa (zobacz także uwagę na końcu tej odpowiedzi, aby uzyskać więcej informacji na temat propozycji standaryzacji takiej sekwencji).

Przypuśćmy mieć klasę (szablon) index_range która zamyka szereg kompilacji liczb całkowitych [m, n) i klasy (szablon) index_list który obudowuje listę kompilacji liczb całkowitych, to jak należałoby użyć im:

template<typename T, typename... Args> 
struct foo 
{ 
    tuple<Args...> args; 

    // Allows deducing an index list argument pack 
    template<size_t... Is> 
    T gen(index_list<Is...> const&) 
    { 
     return T(get<Is>(args)...); // This is the core of the mechanism 
    } 

    T gen() 
    { 
     return gen(
      index_range<0, sizeof...(Args)>() // Builds an index list 
      ); 
    } 
}; 

i tu jest możliwa realizacja index_range i index_list:

//=============================================================================== 
// META-FUNCTIONS FOR CREATING INDEX LISTS 

// The structure that encapsulates index lists 
template <size_t... Is> 
struct index_list 
{ 
}; 

// Collects internal details for generating index ranges [MIN, MAX) 
namespace detail 
{ 
    // Declare primary template for index range builder 
    template <size_t MIN, size_t N, size_t... Is> 
    struct range_builder; 

    // Base step 
    template <size_t MIN, size_t... Is> 
    struct range_builder<MIN, MIN, Is...> 
    { 
     typedef index_list<Is...> type; 
    }; 

    // Induction step 
    template <size_t MIN, size_t N, size_t... Is> 
    struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...> 
    { 
    }; 
} 

// Meta-function that returns a [MIN, MAX) index range 
template<unsigned MIN, unsigned MAX> 
using index_range = typename detail::range_builder<MIN, MAX>::type; 

zauważyć również, że istnieje interesting proposal przez Jonathan Wakelystandaryzuje szablon klasy int_seq, który jest czymś bardzo podobnym do tego, co nazwałem index_list tutaj.

+0

nie próbowałem tego ale wygląda na to, że masz tam jeden błąd: ustawiasz 'MAX' na' sizeof ... (Args) - 1', chociaż to maksimum jest wykluczone z listy.W przeciwnym razie dziękuję za wspaniałą odpowiedź, ponieważ bardzo wyraźnie wyjaśnia poszczególne kroki. – MvG

+0

@MvG: Dokładnie! Przepraszam za to, dobry połów. –

+1

Przednia deklaracja głównego szablonu jest bezcelowa i dezorientująca. Po prostu zdefiniuj "Krok indukcyjny" pierwszy. – Nikolai

1

tworzenia sekwencji indeksy od 0 do n-1:

template<size_t... indexes> 
struct seq {}; 

template<size_t n, size_t... indexes> 
struct make_seq: make_seq<n-1, n-1, indexes...> {}; 

template<size_t... indexes> 
struct make_seq: make_seq<0, indexes...> { 
    typedef seq<indexes...> type; 
}; 

rozpakować równolegle ze swoimi argumentami lub jako indeks do get<> w danym przypadku.

Celem jest coś takiego:

template< typename T, typename Tuple, typename Indexes > 
struct repack; 

template< typename... Ts, size_t... indexes > 
struct repack< tuple<Ts...>, seq<indexes...> > { 
    T operator()(tuple<Ts...> const& args) const { 
    return T(get<indexes>(args)...); 
    } 
}; 

użycie repack w swojej gen tak:

T gen() { 
    repack<T, tuple<Args...>, typename make_seq<sizeof...(Args)>::type> repacker; 
    return repacker(args); 
}  
1

Musisz użyć indices trick, co oznacza warstwę zadnie:

template <std::size_t... Is> 
struct indices {}; 
template <std::size_t N, std::size_t... Is> 
struct build_indices 
    : build_indices<N-1, N-1, Is...> {}; 
template <std::size_t... Is> 
struct build_indices<0, Is...> : indices<Is...> {}; 

template<typename T, typename... Args> 
struct foo { 
    tuple<Args...> args; 
    T gen() { return gen(build_indices<sizeof...(Args)>{}); } 
private: 
    template<std::size_t... Is> 
    T gen(indices<Is...>) { return T(get<Is>(args)...); } 
}; 
+0

Zajęło mi trochę czasu, aby dowiedzieć się, że druga wersja 'build_indices' jest specjalizacją pierwszego, a nie na odwrót. Przeczytanie bardziej wyczerpującej odpowiedzi Andy'ego Prowla z pewnością pomogło. – MvG

2

C++ 14 doda standardową obsługę index_sequence:

template<typename T, typename... Args> 
struct foo { 
    tuple<Args...> args; 
    T gen() { return gen_impl(std::index_sequence_for<Args...>()); } 
private: 
    template <size_t... Indices> 
    T gen_impl(std::index_sequence<Indices...>) { return T(std::get<Indices>(args)...); } 
}; 
Powiązane problemy