Partially specialized template static constexpr in template class
Asked Answered
R

4

6

(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?

Rabelais answered 29/7, 2022 at 12:38 Comment(3)
But why did you declare C as a template? if B is a size_t C is a size_t too. What size_t C<T> = B; is for?Highpitched
What compiler do you use? Clang compiles this without a word of protest.Dendriform
Interesting. I'm using visual studio, and actually I tried clang (but with visual studio also) and it didn't work. @DendriformRabelais
C
2

GCC and MSVC are wrong in rejecting the code as the program is well-formed.

B is a constant expression and can be used as an initializer when initializing C during its partial specialization. This is a msvc bug reported here as:

MSVC rejects valid code when using constexpr static data member as initializer.


Moreover, we are allowed to provide a partial specialization for C(as you did). This is a gcc bug reported as:

GCC rejects valid code involving partial specialization of variable template.

Catholic answered 29/7, 2022 at 17:2 Comment(0)
S
3

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;
};
Stratus answered 29/7, 2022 at 13:0 Comment(2)
Thanks! Can you please explain why my way doesn't work? What is the big difference between (my) A and Works/AlsoWorks? I thought it's possible to partially specialize static fieldsRabelais
"you cant specialize this way": Why not?Baiss
C
2

GCC and MSVC are wrong in rejecting the code as the program is well-formed.

B is a constant expression and can be used as an initializer when initializing C during its partial specialization. This is a msvc bug reported here as:

MSVC rejects valid code when using constexpr static data member as initializer.


Moreover, we are allowed to provide a partial specialization for C(as you did). This is a gcc bug reported as:

GCC rejects valid code involving partial specialization of variable template.

Catholic answered 29/7, 2022 at 17:2 Comment(0)
C
1

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:

Caelian answered 29/7, 2022 at 13:36 Comment(1)
A<int> is not a dependent name, so template can never be required in this context. If the compiler changes its behavior based on that, then it has a bug.Baiss
B
1

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

Baiss answered 29/7, 2022 at 16:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.