Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does a class template's requires clause have to be repeated outside member definitions?

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?

like image 445
PHD Avatar asked Aug 28 '20 06:08

PHD


People also ask

How do you define a template function outside class?

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.

Can a member function be a template?

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.

How many template parameters are allowed in a template classes?

Explanation: Just like normal parameters we can pass more than one or more template parameters to a template class.

Can classes be declared as template?

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.


Video Answer


2 Answers

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.

like image 87
StoryTeller - Unslander Monica Avatar answered Oct 16 '22 16:10

StoryTeller - Unslander Monica


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)

like image 41
dfrib Avatar answered Oct 16 '22 15:10

dfrib