模板参数不完全类型何时完整?C++多编译器行为差异及程序预期
C++ 不完整类型与概念约束的编译器行为分歧问题
当检查a<b>默认构造函数的约束条件时,类型d——进而类型b——是否应被视为完整类型?GCC允许构造b类型的对象,但Clang和MSVC均拒绝该操作。
不仅构造b对象会出现此结果,即使只是为嵌套类型b::d创建别名(即using w = b::d;),也会出现同样的编译器行为差异。
奇怪的是,这种编译器行为差异仅在用户为a类型提供默认构造函数并以c<d>约束时才会出现。请问这段程序的预期执行结果是什么?
template<typename T> concept c = requires { sizeof(T); }; template<typename B> struct a { struct d : B {}; a() requires c<d> = default; }; struct b : a<b> {}; auto v = b{}; // clang 报错, gcc 通过, msvc 报错
Clang 错误信息
<source>:6:16: 错误:基类类型不完整 6 | struct d : B {}; | ^ <source>:2:24: 注意:此处请求实例化成员类 'a<b>::d' 2 | concept c = requires { sizeof(T); }; | ^ <source>:2:24: 注意:此处实例化需求 2 | concept c = requires { sizeof(T); }; | ^~~~~~~~~ <source>:2:13: 注意:在此处将模板参数代入约束表达式时 2 | concept c = requires { sizeof(T); }; | ^~~~~~~~~~~~~~~~~~~~~~~ <source>:7:18: 注意:在此处检查概念 'c<a<b>::d>' 的满足性时 7 | a() requires c<d> = default; | ^~~~ <source>:7:18: 注意:在此处将模板参数代入约束表达式时 7 | a() requires c<d> = default; | ^~~~ <source>:7:5: 注意:在此处检查函数 'a' 的约束满足性时 7 | a() requires c<d> = default; | ^ <source>:9:12: 注意:在此处请求实例化模板类 'a<b>' 时 9 | struct b : a<b> {}; | ^ <source>:9:8: 注意:'b' 的定义直到闭合 '}' 才完成 9 | struct b : a<b> {}; | ^ <source>:10:12: 错误:没有匹配的构造函数用于初始化 'a<b>' 10 | auto v = b{}; | ^ <source>:7:5: 注意:候选构造函数不可行:约束未满足 7 | a() requires c<d> = default; | ^ <source>:7:18: 注意:因为 'd' 不满足 'c' 7 | a() requires c<d> = default; | ^~~~ <source>:2:24: 注意:因为 'sizeof(T)' 无效 2 | concept c = requires { sizeof(T); }; | ^ <source>:5:8: 注意:候选构造函数(隐式复制构造函数)不可行:需要1个参数,但提供了0个 5 | struct a { | ^ <source>:5:8: 注意:候选构造函数(隐式移动构造函数)不可行:需要1个参数,但提供了0个 5 | struct a { | ^
MSVC 错误信息
<source>(6): 错误 C2504: “b”: 基类未定义 <source>(6): 注意:模板实例化上下文(最早的在前)是 <source>(7): 注意:在评估概念 'c<a<b>::d>' 时 <source>(6): 注意:在编译类 'a<b>::d' 时
预期执行结果
根据C++标准规则,这段程序应该编译失败,Clang和MSVC的行为符合标准,GCC的行为存在偏差。
具体原因:
- 定义
struct b : a<b> {};时,实例化基类a<b>的阶段,b的定义尚未完成(要到闭合的}才会成为完整类型),此时b属于不完整类型。 a<B>::d继承自B(即b),而C++要求继承操作的基类必须是完整类型。检查默认构造函数的约束c<d>时,需要验证sizeof(d)的有效性,这要求d是完整类型,但此时d的基类b仍不完整,导致d无法成为完整类型,sizeof(d)操作非法。- 因此
c<d>的约束无法满足,a<b>的默认构造函数不可用,而b的默认构造函数依赖于基类的默认构造函数,最终无法初始化b对象。
内容的提问来源于stack exchange,提问作者303
相关产品推荐
相关产品推荐

