When the member of a class template that uses the requires
clause is defined outside the class, gcc
does not complain if requires
is not specified, whereas clang
does.
Consider the code snippet below:
#include <concepts>
template<typename Container>
requires std::integral<typename Container::value_type>
class Foo {
public:
void func();
};
template<typename Container>
void Foo<Container>::func()
{}
The compilation using gcc
does not complain.
While clang
reports the following error:
❯ clang++ -std=c++2a test.cpp
test.cpp:10:1: error: requires clause differs in template redeclaration
template<typename Container>
^
test.cpp:4:19: note: previous template declaration is here
requires std::integral<typename Container::value_type>
^
1 error generated.
If I change the definition as below:
template<typename Container>
requires std::integral<typename Container::value_type>
void Foo<Container>::func()
{}
now clang
does not complain.
Output from gcc --version
:
gcc (GCC) 10.2.0
Output from clang --version
:
clang version 10.0.1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Is this a bug to be reported?
It's quite common to separate a template into 2 files, one being a traditional header, and the second being the implementation, as with non-templated functions and their implementation. The only difference is that you need to #include the template implementation file as well as the header when you want to use it.
Member functions can be function templates in several contexts. All functions of class templates are generic but are not referred to as member templates or member function templates. If these member functions take their own template arguments, they are considered to be member function templates.
Explanation: Just like normal parameters we can pass more than one or more template parameters to a template class.
A class template must be declared before any instantiation of a corresponding template class. A class template definition can only appear once in any single translation unit. A class template must be defined before any use of a template class that requires the size of the class or refers to members of the class.
A bug should be filed for GCC, because it accepts the code, even though the declaration of the member outside the class does not have an equivalent template-head.
[temp.class]
3 When a member function, a member class, a member enumeration, a static data member or a member template of a class template is defined outside of the class template definition, the member definition is defined as a template definition in which the template-head is equivalent to that of the class template ([temp.over.link]).
[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.
The equivalence of templates-heads requires that both have an equivalent requires clause. Omitting it entirely breaks the equivalence.
From [temp.mem.func]/1 [extract, emphasis mine]:
A member function of a class template may be defined outside of the class template definition in which it is declared. [Example:
A constrained member function can be defined out of line:
template<typename T> concept C = requires { typename T::type; }; 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>
— end example]
taking note particularly the final example of the (non-normative) text.
Thus, Clang is correct to reject whereas GCC is wrong to accept the first program as the out-of-line definition
template<typename Container> void Foo<Container>::func() {}
does not match any function in Foo<Container>
.
(I have not yet found an open GCC bug report for this)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With