构造d{int{},int{}}时是否需生成隐式推导指南实例化d<int>?C++20标准解析
类模板隐式推导指南:
d{int{}, int{}} 是否应推导为 d<int>? 问题核心
当使用d{int{}, int{}}构造对象时,编译器是否应当生成隐式类模板推导指南以允许实例化d<int>?C++20标准对此有何明确规定?
代码示例
#include <concepts> struct b { b(auto, auto...) {} }; template<typename> struct d : b { using b::b; }; // Clang编译通过,GCC、MSVC报错 static_assert(std::same_as<decltype(d{int{}, int{}}), d<int>>);
编译器行为差异
- Clang:接受代码,认为可推导出
d<int> - GCC:报错,提示类模板参数推导失败,找不到匹配的
d(int, int)构造函数 - MSVC:报错,无法推导
d的模板参数,且提示构造函数参数数量不匹配
GCC错误摘要
<source>:5:51: error: class template argument deduction failed: <source>:5:51: error: no matching function for call to 'd(int, int)' // 后续提示候选推导指南仅接受0个参数或d<T>类型参数,无法匹配传入的int参数
MSVC错误摘要
<source>(5): error C2641: cannot deduce template arguments for 'd' <source>(5): error C2780: 'd<<unnamed-symbol>> d(b &&)': expects 1 arguments - 2 provided // 后续提示无法推导模板参数,且多个候选构造函数参数数量不匹配
C++20标准规定分析
根据C++20标准[over.match.class.deduct]章节规则:
- 类模板推导(CTAD)的隐式推导指南从类的构造函数(含继承的构造函数)生成,逻辑等价于将构造函数包装为包含类模板参数、构造函数自身模板参数的模板函数。
- 对于继承的构造函数,生成的推导指南会将基类构造函数签名中的基类类型替换为派生类模板的特化类型,但派生类的模板参数必须与构造函数的参数/返回类型建立明确关联,才能完成推导。
回到示例代码:
- 基类
b的构造函数是模板构造函数(auto参数等价于模板参数),但派生类d<T>的模板参数T并未出现在继承的构造函数签名中——既不是构造函数的参数类型,也未与参数类型产生绑定关系。 - 标准未规定编译器可“默认将构造函数第一个参数的类型作为派生类模板参数”,因此不存在合法推导路径将
d{int{}, int{}}与d<int>关联。
结论
根据C++20标准的严格解读,不应当生成允许实例化d<int>的隐式类模板推导指南。Clang的行为属于编译器非标准扩展,GCC和MSVC的报错符合标准要求。若需实现此类推导,需显式提供推导指南:
template<typename T, typename... Args> d(T, Args...) -> d<T>;
内容的提问来源于stack exchange,提问作者303
相关产品推荐
相关产品推荐

