Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

out of line definition of guided constructor (c++20)

g++ happily accepts the following code, whereas clang nor msvc are able to match out of line definitions.

Any idea why ?

template <bool B>
struct test 
{
    test() requires (B);
    test() requires(!B);
};


template <>
test<true>::test()
{}

template <>
test<false>::test()
{}

int main()
{
    test<false> a;
    test<true> b;
    return 0;
}

Demo

Clang:

error: out-of-line definition of 'test' does not match any declaration in 'test<true>'

Msvc:

error C2244: 'test<true>::test': unable to match function definition to an existing declaration

like image 348
reder Avatar asked Apr 01 '21 12:04

reder


Video Answer


1 Answers

You're declaring constrained constructors but defining two unconstrained specializations. Those won't ever match.

What you probably meant was:

template <bool B>
struct test
{
    test() requires (B);
    test() requires(!B);
};

template <bool B>
test<B>::test() requires (B)
{}

template <bool B>
test<B>::test() requires (!B)
{}

This compiles fine in all 3 compilers.

As to why your original version compiles - it's a GCC bug 96830. Clang is right, code is ill-formed because the out-of-line definitions don't match the template definition (note also that template<> ... is full specialization syntax).

See [temp.class.general]/3 (emphasis mine):

When a member of a class template is defined outside of the class template definition, the member definition is defined as a template definition with the template-head equivalent to that of the class template.

[temp.over.link]/6:

Two template-heads are equivalent if their template-parameter-lists have the same length, corresponding template-parameters are equivalent and are both declared with type-constraints that are equivalent if either template-parameter is declared with a type-constraint, and if either template-head has a requires-clause, they both have requires-clauses and the corresponding constraint-expressions are equivalent.

Also see [temp.mem.func]/1 for an example of declaring a constrained member out-of-line:

template<typename T> struct S {
    void f() requires C<T>;
    void g() requires C<T>;
};

template<typename T>
void S<T>::f() requires C<T> { }      // OK
template<typename T>
void S<T>::g() { }                    // error: no matching function in S<T>
like image 190
rustyx Avatar answered Sep 20 '22 00:09

rustyx