Clang拒绝含涉及类自身的requires子句的模板构造函数类外定义是否合规?
Clang拒绝含自身类型requires子句的模板构造函数是否符合C++标准?
问题重现
首先看一段可正常编译的、类外定义模板构造函数的结构体代码:
template <typename T> struct Foo { template <typename F> Foo(F f); }; template <typename T> template <typename F> Foo<T>::Foo(F f) {}
在-std=c++20标准下,Clang可正常编译这段代码。但当为模板构造函数添加涉及结构体自身类型的requires子句后,Clang会报错,而GCC可正常接受:
报错代码示例:
#include <concepts> template <typename T> struct Foo { template <typename F> requires (!std::same_as<Foo<T>, int>) Foo(F f); }; template <typename T> template <typename F> requires (!std::same_as<Foo<T>, int>) Foo<T>::Foo(F f) {}
Clang报错信息:
<source>:13:9: error: out-of-line definition of 'Foo<T>' does not match any declaration in 'Foo<T>' Foo<T>::Foo(F f) {} ^~~
核心疑问
Clang拒绝此代码是否符合C++标准?若符合,标准中哪一部分对此有规定?
补充背景:添加涉及待构造类型的requires子句,是为了区分构造函数与移动构造函数,避免当F与Foo类型相同时,后续requires表达式因递归依赖无法求值。实际代码更复杂,且接受F的转发引用。
解答
Clang的行为不符合C++标准,GCC的处理是正确的。
根据C++20标准中[temp.spec.match](即13.7.6.1节,模板声明与定义的匹配)的规定:类模板的成员模板的类外定义,其模板参数列表、requires子句等必须与类内声明实质等价。此处类内声明和类外定义的requires子句完全一致,都是!std::same_as<Foo<T>, int>,不存在实质差异,应当被视为匹配。
Clang此处的报错属于编译器实现层面的问题,未能正确处理requires子句中引用类模板自身实例化类型的场景。
内容的提问来源于stack exchange,提问作者jacobsa
相关产品推荐
相关产品推荐

