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 m.s.) ---
--- 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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With