c++templatesstaticconstexprnon-type

Using static constexpr member array as pointer in template parameters


The following main.cpp illustrates the problem:

#include <type_traits>

template <class T, std::size_t N>
struct Array
{
    T data_[N];
};

template <const std::size_t* EltArray, std::size_t EltCount>
struct Foo
{

};

int main()
{
    // SIDE NOTE if arr is not declared static: the address of 'arr' is not a valid template argument
    // because it does not have static storage duration
    static constexpr std::size_t arr[3] = {1, 2, 3};
    Foo<arr, 3> foo;// WORKING

    static constexpr Array<std::size_t, 3> arr2 = {1, 2, 3};
    static constexpr const std::size_t* arr2_ptr = arr2.data_;
    Foo<arr2_ptr, 3> foo2;// ERROR:
    // 'arr2_ptr' is not a valid template argument of type 'const size_t*'
    // {aka 'const long long unsigned int*'} because
    // 'arr2.Array<long long unsigned int, 3>::data_' is not a variable

    static constexpr const std::size_t* test = std::integral_constant<const std::size_t*, arr2_ptr>{};// ERROR:
    // 'arr2_ptr' is not a valid template argument of type 'const long long unsigned int*' because
    // 'arr2.Array<long long unsigned int, 3>::data_' is not a variable

    return 0;
}

I don't understand why arr2.data_ is not reusable just like arr. Can someone explain ?

I'm using gcc: mingw-w64\x86_64-8.1.0-posix-sjlj-rt_v6-rev0

g++.exe -Wall -std=c++2a -fconcepts -O2


Solution

  • I want to share the answer i just found in open-std and a compliant solution.

    We all know that we cannot pass any variable as non type.

    Did you know that we can pass a const reference to anything we want ?!

    So the solution is:

    #include <array>
    
    // When passing std::array<std::size_t, EltCount> (by value), i get the error:
    // 'struct std::array<long long unsigned int, EltCount>' is not a valid type for a template non-type parameter
    
    template <std::size_t EltCount, const std::array<std::size_t, EltCount>& Elts>
    struct Foo {};
    
    static constexpr std::array<std::size_t, 3> arr = {1, 2, 3};
    
    int main()
    {
        Foo<3, arr> foo;// WORKING
    
        return 0;
    }
    

    And the answer to the initial question is:

    quote of the N4296

    14.3.2 Template non-type arguments [temp.arg.nontype]

    For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of): (1.1) — a subobject (1.8),

    Moral of the story: we can do what we want with references, not with pointers.