Pregunta

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?

¿Fue útil?

Solución

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).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top