问题描述
在类之外定义了使用requires
子句的类模板的成员时,如果未指定gcc
,requires
不会抱怨,而clang
会声明
请考虑以下代码段:
#include <concepts>
template<typename Container>
requires std::integral<typename Container::value_type>
class Foo {
public:
void func();
};
template<typename Container>
void Foo<Container>::func()
{}
使用gcc
进行编译不会产生错误。
clang
报告以下错误:
❯ 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.
如果我更改定义如下:
template<typename Container>
requires std::integral<typename Container::value_type>
void Foo<Container>::func()
{}
现在clang
没有抱怨。
gcc --version
的输出:
gcc (GCC) 10.2.0
clang --version
的输出:
clang version 10.0.1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
解决方法
即使该类外部成员的声明没有等效的模板头,也应该向GCC提交错误,因为它会接受代码。
[临时班级]
3当成员函数,成员类,成员枚举, 静态数据成员或类模板的成员模板是 在类模板定义之外定义,成员定义被定义为模板定义,其中 template-head 等同于类模板的([temp.over.link])。
[临时链接]
6如果两个模板头相同,则它们是等效的 模板参数列表具有相同的长度,对应 template-parameters是等效的,并且都用 类型约束,如果其中一个模板参数为 用类型约束声明,如果任何一个模板头都有一个 要求条款,它们都有要求条款,并且 相应的约束表达式是等效的。
等价的模板头要求它们都具有一个等效的require子句。完全省略它会等效。
,来自[temp.mem.func]/1 [摘录,重点我的]:
类模板的成员函数可以在 声明它的类模板定义。 [示例:
可以
约束成员函数来定义: 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>
— 最终示例]
特别注意(非规范性)文本的最终示例。
因此,Clang拒绝是正确的,而GCC拒绝接受第一个程序作为线外定义是错误的
template<typename Container> void Foo<Container>::func() {}
与Foo<Container>
中的任何功能都不匹配。
(我尚未为此找到公开的GCC错误报告)