Home > Software design >  Writing compile time recursively resolved types in C : why does one method work and the other fails
Writing compile time recursively resolved types in C : why does one method work and the other fails

Time:11-15

I have been dabeling with a bit of template meta-programing...

Why does this work

template<std::size_t Dimension, typename Type, std::size_t Size>
struct ArrayType
{
public:
    ArrayType(void) = delete;
    ~ArrayType(void) = delete;

private:
    template<std::size_t Dim, typename T, std::size_t N>
    struct ArrayType_Impl
    {
        std::array<decltype(ArrayType_Impl<Dim - 1, T, N>::type), Size> type;
    };

    template<typename T, std::size_t N>
    struct ArrayType_Impl<1 , T, N>
    {
        std::array<T, N> type;
    };

public:
    //USE THIS
    using get_type = decltype(ArrayType_Impl<Dimension, Type, Size>::type);
};

But this doesn't!!!

template<std::size_t Dimension, typename Type, std::size_t Size>
struct Non_Working_Array
{
    using type = std::array<Non_Working_Array<Dimension - 1, Type, Size>::type, Size>;
};

template<typename Type, std::size_t Size>
struct Non_Working_Array<1, Type, Size>
{
    using type = std::array<Type, Size>;
};

This is gcc 9.3.0's resulting error when compiling the above broken code:

multi_dimensional_array.cpp:11:85: error: type/value mismatch at argument 1 in template parameter list for ‘template<class _Tp, long unsigned int _Nm> struct std::array’ 11 | using type = std::array<Non_Working_Array<Dimension - 1, Type, Size>::type, Size>; | ^ multi_dimensional_array.cpp:11:85: note: expected a type, got ‘Non_Working_Array<(Dimension - 1), Type, Size>::type’

At the end of the day, the only real difference other than packaging of the respective structs, is that the working one makes an instance of the type and uses decltype to get the type information needed, while the other seeks to make an alias with using keyword that is recursively defined.

Thank you in advance.

CodePudding user response:

I think the root cause of the problem is that, as the compiler states, Non_Working_Array<(Dimension - 1), Type, Size>::type is not seen as a type. The part that I did not initially understand is how to fix this. Now I do. Adding typename is close to the solution, but not just it.

This is the new code:

template<std::size_t Dimension, typename Type, std::size_t Size>
struct Now_Working_Array
{
private:
    using type_private = typename Now_Working_Array<Dimension - 1, Type, Size>::type;

public:
    using type = std::array<Now_Working_Array::type_private, Size>;
};

template<typename Type, std::size_t Size>
struct Now_Working_Array<1, Type, Size>
{
    using type = std::array<Type, Size>;
};

I believe that the reason this now works is that you take out the part that was questioned about being a type, use typename to say its a type, and the shove it into std::array!

CodePudding user response:

In many contexts, dependent names are assumed to be values. So Non_Working_Array<Dimension - 1, Type, Size>::type is assumed to be a value. So you get an error. A few spots have been added where syntactically only types are allowed; but other than those cases, you need to add a typename to disambiguate.

typename Non_Working_Array<Dimension - 1, Type, Size>::type,

The full fix being

using type = std::array<typename Non_Working_Array<Dimension - 1, Type, Size>::type, Size>;

now we can clean thjs up a bit.

template<std::size_t Dim, typename T, std::size_t N>
struct RecursiveArray;
template<std::size_t Dim, typename T, std::size_t N>
using RecursiveArray_t=typename RecursiveArray<Dim,T, N>::type;


template<std::size_t Dim, typename T, std::size_t N>
struct RecursiveArray {
    using type=std::array<RecursiveArray_t<Dim,T,N>, Size>;
};
template<stypename T, std::size_t N>
struct RecursiveArray<0,T,N> {
    using type=T;
};

as N^0 is 1, a single element.

Consumers now use RecursiveArray_t<3,int,4>.

A more general one might use something like Array_t<int,4,4,4> as an intermediate.

  • Related