Why does an out-of-class member template definition need a repetition of its declaration 'requires-clause'

Viewed 233

This is related to the following question: Does a class template's requires clause have to be repeated outside member definitions?

In other words, given the code for the following templated struct A, the requires-clause needs to be repeated for the foo() function definition (per the standard paragraphs quoted in the linked question above)

template <typename T>
    requires std::integral<T> //'requires-clause'
struct A
{
    void foo();
};

template <typename T>
    requires std::integral<T> //This 'requires-clause' is required by the standard to be reiterated
void A<T>::foo()
{
    //
}

//Specialisations do not require explicit 'requires-clause'
template <>
void A<int>::foo()
{
    //
}

//Specialisation with a type that does not meet the constraint raises a compile-time error
template <>
void A<double>::foo() //Not allowed because std::integral(double) == false
{
    //
}

But I don't know why it is necessary to repeat the requires-clause for the specific class members described in [temp.class]\3. I can't think of a situation where this requirement would make a difference. In the example above, if the requires-clause is with the struct definition, any attempt to instantiate anything that isn't an integral type would not compile, so whether A<T>::foo() also requires that std::integral<T> == true is irrelevant. AFAIK it also makes no difference during specialisation because any attempt to specialise to a different type from an integral (as in this example) leads to an compilation error.

However, I'm sure that there are legitimate reasons for including this requirement in the standard - can anybody demonstrate a situation where the absence of this requires-clause on the definition would cause an issue?

1 Answers

According to the last section of https://en.cppreference.com/w/cpp/language/constraints you can specialize templates not only on parameters but also on their constraints. Like parameter specializations the standard defines also a partial ordering of constraints, such that a template specialization might be more constrained than another.

So, for example, you may have a more constrained specialization of your template A:

template <typename T>
    requires std::unsigned_integral<T>
struct A
{
    void foo();
};

With a different implementation of the element function foo:

template <typename T>
    requires std::unsigned_integral<T>
void A<T>::foo()
{
    // different implementation of foo() relying on T being unsigned integral
}

So, without the repeated requires clause in the definitions of both versions of A<T>::foo the compiler would not know to which specialization of A the defined foo would belong.

Related