类类型非类型模板参数代码:GCC/MSVC通过Clang拒绝,哪个编译器合规?
哪个编译器的处理是正确的?
Clang的处理是正确的,MSVC和GCC在此处的行为不符合C++20标准。
核心原因
C++20允许类类型作为非类型模板参数,但这类类必须是结构类型(structural type)。根据标准,结构类型的字面类需满足几个关键条件:
- 不能有用户声明的自定义构造函数(仅允许默认、复制、移动构造函数)
- 所有非静态数据成员和基类都属于结构类型
- 所有构造函数都是constexpr
- 不存在虚拟函数或虚拟基类
你的代码里,Impl定义了一个接受std::initializer_list<int>的自定义构造函数,这直接导致Impl不再符合结构类型的要求,因此不能被用作非类型模板参数的类型。Clang严格遵循标准规则拒绝了代码,而MSVC和GCC在这里做了非标准的扩展,允许了不符合要求的用法。
符合标准的修正示例
要让代码合法,需要把Impl调整为符合结构类型的类,最简单的方式是将其改为聚合类型(移除自定义构造函数,使用聚合初始化):
struct Impl { int data[4]; // 公开的非静态数据成员,无自定义构造 }; struct Bar{}; template<typename T, Impl impl> struct Foo{}; int main() { constexpr Foo<Bar, {{1,2,3,4}}> foo; // 聚合初始化,符合C++20标准 return 0; }
或者如果需要固定数量的成员,也可以直接定义独立的公开成员:
struct Impl { int a, b, c, d; }; struct Bar{}; template<typename T, Impl impl> struct Foo{}; int main() { constexpr Foo<Bar, {1,2,3,4}> foo; // 聚合初始化,合法 return 0; }
内容的提问来源于stack exchange,提问作者user19414420
相关产品推荐
相关产品推荐

