Reputation: 4007
I was trying to write a class that could act as a compile-time array, using some TMP and constexpr C++11 magic. The end-goal I was trying to achieve is basically being able to write
Type array[Size] = {X, X, X, ..., X};
as
Array<Type, Size, X> array;
For arbitrary Type
and Size
(only limited by the compiler's recursion depth). This is what I came up with:
template <typename T, size_t Size, T Value = T()>
struct Array
{
template <size_t Count, T ... Elements>
struct Helper
{
constexpr Helper() {}
Helper<Count + 1, Value, Elements...> helper;
constexpr T const *get() const
{
return helper.get();
}
};
template <T ... Elements>
struct Helper<Size, Elements ...>
{
constexpr Helper() {}
T array[Size] {Elements...};
constexpr T const * get() const
{
return array;
}
};
constexpr Array():
helper(),
array{helper.get()}
{}
Helper<0> helper;
T const *array;
constexpr operator T const * ()
{
return array;
}
};
This is what happens: the Array
object initializes a member of type Helper<0>
, which itself initializes a member of type Helper<1>
, which initializes a member of type Helper<2>
which ...
, adding one element in each iteraton until the specialized template for Helper<Size, ...>
is instantiated. By now, the variadic pack contains exactly Size
elements, which can be used in a brace-enclosed initializer list to initialize the array contained in the final Helper
object. This is where the mysterious error comes in:
error: ‘Elements’ was not declared in this scope
I'm sorry? Not declared? It's right there as a template parameter. Am I overlooking something very obvious?
Upvotes: 0
Views: 138
Reputation: 218323
As a workaround, moving Helper
outside of template class works.
Following may help:
namespace detail {
template <typename T, T value, T ... Elements>
struct ArrayHelper
{
constexpr ArrayHelper() {}
T array[sizeof...(Elements)] {Elements...};
constexpr T const * get() const { return array; }
};
template <std::size_t Size, typename T, T value, T ... Elements>
struct MakeArrayHelper
{
typedef typename MakeArrayHelper<Size - 1, T, value, value, Elements...>::type type;
};
template <typename T, T value, T ... Elements>
struct MakeArrayHelper<0, T, value, Elements...>
{
typedef ArrayHelper<T, Elements...> type;
};
} // namespace detail
template <typename T, size_t Size, T Value = T()>
struct Array
{
constexpr Array():
helper(),
array{helper.get()}
{}
typename detail::MakeArrayHelper<Size, T, Value>::type helper;
T const *array;
constexpr operator T const * () const { return array; }
};
Note that MakeArrayHelper
may be rewritten with recursion with Size / 2
instead of Size - 1
to allow bigger size (And so reach recursion limit for bigger Size).
Upvotes: 1