C++20非类型模板参数推导问题及跨编译器兼容方案问询
问题2:为何MSVC接受特定语法而其他编译器不允许?是否属于实现错误?兼容三大编译器的方案有哪些?
对应代码:
template<typename = int> struct s { template<typename = int> struct n {}; template<n<typename> = n{}> constexpr int f(); }; static_assert(requires { s{}.f(); }); // clang、gcc报错,MSVC通过
注:排除添加成员类型并使用requires子句进行手动类型约束的繁琐方案。
解答
语法合法性与MSVC实现问题
n<typename>的写法不符合C++标准:在模板参数列表中,typename仅能用于声明模板类型参数,或在依赖类型前消除歧义,不能直接作为模板实参传递给n。因此Clang和GCC的报错是符合标准的,MSVC接受该语法属于编译器实现错误——它错误地将typename解析为n的默认模板实参(即n<int>),从而允许了这一非法语法。
兼容三大编译器的方案(除了将参数类型改为auto)
可以通过显式指定n的默认模板实参来避免类型推导歧义,写法符合标准且三大编译器均可支持:
template<typename = int> struct s { template<typename = int> struct n {}; // 显式指定非类型模板参数的类型为n<>(使用n的默认模板实参) template<n<> = n{}> constexpr int f(); }; static_assert(requires { s{}.f(); }); // Clang、GCC、MSVC均可通过
这里n<>明确使用了n模板的默认模板实参(int),非类型模板参数的类型被显式指定,无需编译器进行推导,因此能在三大编译器中正常编译。
MSVC报错信息(翻译后):
<source>(9): error C2672: 's<int>::f': 未找到匹配的重载函数 <source>(6): note: 候选函数为: 'int s<int>::f(void)' <source>(9): note: 'int s<int>::f(void)': 无法推导模板参数 '__formal' <source>(9): error C2607: 静态断言失败
内容的提问来源于stack exchange,提问作者303
相关产品推荐
相关产品推荐

