简体   繁体   中英

How to create a compile-time static class type that initializes a member container with specific values?

This is basically what I want:

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

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

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

Doesn't have to be a deque , I just want to associate an iterable list of values with a type . So that I can do stuff like

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

Usually it's making things dynamic that's the problem, but apparently it's just as hard to to make some dynamic things to be compile-time. I do not want to use inheritance, polymorphism and similar runtime, overhead-inducing approaches.

You cannot have a compile-time list of user-defined structs. And you cannot have a compile-time std::string of any kind . It is not a literal type , and therefore cannot be used in any constexpr context.

If you restrict yourself to types that can be used in non-type template parameters , then you can use a variadic template type. And then, you don't have to bother with the runtime container:

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 .

However, be advised that compilers have fairly strict limits on the number of template parameters a type can have. You probably won't blow past this limit by typing them in on a keyboard, but you can construct them past it.

What about using variadic template non type arguments?

Something like

--- edit: corrected (thanks ms) ---

--- edit2: added array _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;
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM