2016-06-18 13 views
5

Questo è fondamentalmente ciò che voglio:Come creare un tipo di classe statica in fase di compilazione che inizializza un contenitore membro con valori specifici?

struct Item{ 
    int id; 
    std::string s; 
}; 

template <???> 
struct StaticContainer 
{ 
    static const std::deque<Item> _items; 
}; 

template <???> 
const std::deque<Item> StaticContainer<>::_items {???}; 

non deve essere un deque, voglio solo associare un elenco di valori iterabile con un type. In modo che io possa fare cose come

typedef StaticContainer<{{1, "a", {2, "b"}, {3, "c"}}> T1; 
typedef StaticContainer<{{0, "e"}, {-1, "f"}, {-2, "g"}}> T2; 

int main() { 
    T1 t1; 
    T2 t2; 
    t1 = t2; // Error: incompatible types 

    return 0; 
} 

Di solito si sta facendo le cose dinamica che è il problema, ma a quanto pare è altrettanto difficile per fare alcune cose per essere dinamici di compilazione. Non voglio usare ereditarietà, polimorfismo e runtime simile, approcci che inducono al sovraccarico.

+1

solo utilizzare parametri di modello non di tipo: http://coliru.stacked-crooked.com/a/47747401cd58d84c –

+0

@ m.s .: ma come inizializzare '_items', quindi? Ho bisogno di mettere i valori di lista di questo tipo creati lì. –

+0

Se si desidera un elenco di valori in fase di compilazione, perché è necessario memorizzarli in un * contenitore *? –

risposta

5

È non può avere un elenco compilato di strutture definite dall'utente. E non puoi avere un tempo di compilazione std::stringdi qualsiasi tipo. Non è un literal type e pertanto non può essere utilizzato in alcun contesto constexpr.

Se ci si limita ai tipi che possono essere utilizzati in non-type template parameters, è possibile utilizzare un tipo di modello variadic. E poi, non c'è bisogno di perdere tempo con il contenitore di runtime:

template<typename T, T ...ts> 
struct value_list 
{ 
    //Not the most efficient way to get a value. 
    template<int ix> 
    static constexpr auto get() 
    { return std::get<ix>(std::make_tuple(ts...)); } 

    //Get a general container 
    template<typename Container> 
    static auto get_container() {return Container{ts...};} 

    //Get an array, appropriately sized. 
    static constexpr auto get_array() 
    { return std::array<T, sizeof...(ts)>{ts...}; } 

    //Manipulators. Types are static, so they must return 
    //new types with the new values. 
    template<T new_val> 
    constexpr auto push_back() 
    {return value_list<T, ts..., new_val>();} 

    template<T new_val> 
    constexpr auto push_front() 
    {return value_list<T, new_val, ts...>();} 
}; 

Live example.

Tuttavia, si noti che i compilatori hanno limiti piuttosto rigidi sul numero di parametri del modello che un tipo può avere. Probabilmente non supererai questo limite digitandoli su una tastiera, ma puoi costringerli a passarlo.

+0

E se il mio 'Item' non è solo un' int' ma una 'struct' di' int' e 'std: : string', sono completamente sfortunato? –

+0

@VioletGiraffe: c'era qualche ambiguità nella mia prima frase? Se si desidera avere liste di * valori * che sono definiti * e usati * in fase di compilazione, devono essere elementi che possono essere inseriti in elenchi di argomenti modello non di tipo. E 'std :: string' non è nemmeno un tipo letterale: tu * non puoi possibilmente * avere un valore di questo tipo in fase di compilazione. –

2

Che dire dell'uso di argomenti di tipo non parametrico variadic?

qualcosa come

--- Edit: corretto (ms) grazie ---

--- EDIT2: aggiunto matrice _items2 ---

#include <deque> 
#include <array> 
#include <iostream> 

struct Item{ 
    int id; 
}; 

template <int ... vals> 
struct StaticContainer 
{ 
    static const std::deque<Item> _items; 
    static const std::array<Item, sizeof...(vals)> _items2; 
}; 

template <int ... vals> 
const std::deque<Item> StaticContainer<vals...>::_items { {vals}... }; 

template <int ... vals> 
const std::array<Item, sizeof...(vals)> StaticContainer<vals...>::_items2 { { {vals} ... } }; 

typedef StaticContainer<1, 2, 3> T1; 
typedef StaticContainer<0, -1, -2> T2; 

int main() 
{ 
    T1 t1; 
    T2 t2; 
    //t1 = t2; // Error: incompatible types 

    std::cout << T1::_items[1].id << std::endl; 
    std::cout << T2::_items2[0].id << std::endl; 

    return 0; 
} 
+0

L'OP ha chiesto esplicitamente di usare una struct ('Item') definita dall'utente come tipo del contenitore, non' int'. –

+1

non viene compilato per me: http://coliru.stacked-crooked.com/a/b6bc6d6b9a0cb361 –

+0

@ m.s. - hai ragione; corretta; Grazie; ma dovresti coutare 'T1 :: _ items [1] .id' – max66