Partially specialized template static constexpr in template class

Viewed 108

(A toy, minimal not-working example)

The next code won't compile with cpp20/17:

template<typename TSomeTemplate>
struct A
{
    static constexpr size_t B = 1;

    template<typename T, typename... Ts>
    static constexpr size_t C = 4;

    template<typename T>
    static constexpr size_t C<T> = B;
};

int main()
{
    A<int>::C<int>;

    return 0;
}

With the error of "Expression did not evaluate to constant" regarding the last line in the struct. However, it does compile with full specialization, or without referencing B:

template<typename TSomeTemplate>
struct Works
{
    static constexpr size_t B = 1;

    template<typename T, typename... Ts>
    static constexpr size_t C = 4;

    template<>
    static constexpr size_t C<int> = B;
};

template<typename TSomeTemplate>
struct AlsoWorks
{
    static constexpr size_t B = 1;

    template<typename T, typename... Ts>
    static constexpr size_t C = 4;

    template<typename T>
    static constexpr size_t C<T> = 2;
};

Why is it so? How can I make the first struct work?

4 Answers

You cant specialize this way because of a bug in gcc (see Anoop's answer), you could use SFINAE or you may try something like:

template<typename TSomeTemplate>
struct A
{
    static constexpr size_t B = 1;

    template<typename T, typename... Ts>
    static constexpr size_t C = sizeof...(Ts) == 0 ? B : 4;
};

I was able to compile your code by adding template keyword.

template<typename TSomeTemplate>
struct A
{
    static constexpr size_t B = 1;

    template<typename T, typename... Ts>
    static constexpr size_t C = 4;

    template<typename T>
    static constexpr size_t C<T> = B;
};

int main()
{
    A<int>::template C<int>;

    return 0;
}

As cited here.

Similarly, in a template definition, a dependent name that is not a member of the current instantiation is not considered to be a template name unless the disambiguation keyword template is used or unless it was already established as a template name:

I don't see anything wrong with the code.

template<typename T>
static constexpr size_t C<T> = B;

This is a partial specialization of the C variable template, which is allowed, also inside the class scope. It is also more specialized than the primary template.

B is clearly a constant expression since it has been defined before as constexpr variable. Even if it was declared only as const instead it would still be usable in a constant expression (because it is of integral type). The compiler is wrong to reject it as not a constant expression.

It seems there are simply a few compiler bugs at play here (GCC seems to not like the variable template partial specialization inside the class scope and MSVC seems to have an issue with the constness of B. Clang does not complain.)

Related