为何复制推导候选需作为独立的模板推导指南存在?
模板类复制初始化的推导规则疑惑
首先看这段代码:
template <typename T> struct A { A(T); A(const A&); }; int main() { A x(42); // #1 A y = x; // #2 }
你的理解与疑问
你提到:
- #1中
T通过第一个构造函数的隐式推导指南得出,随后用该构造函数初始化x,这部分是完全正确的。 - #2中
T通过复制推导候选推导得出,再调用复制构造函数初始化y,但你疑惑为什么不能通过复制构造函数的隐式推导指南来推导T,同时不理解复制推导候选的用途。
问题解答
这是C++标准特意设计的推导规则,咱们一步步拆解:
1. 为什么复制构造函数不会生成隐式推导指南?
C++标准明确规定:编译器只会为非复制/非移动构造函数生成隐式推导指南。你的A(const A&)属于复制构造函数,被排除在隐式推导指南的生成范围之外。
这么设计的核心原因是避免循环推导和歧义:复制构造函数的参数类型const A&本身依赖于模板参数T,如果用它来推导T,就会陷入“要推导T必须先知道Atemplate <typename U> A(const A<U>&)),编译器根本无法确定该推导哪个类型作为T,会直接导致歧义。
2. 复制推导候选的作用是什么?
复制推导候选是标准为模板类复制/移动初始化专门设计的特殊推导规则,它的核心逻辑是:当用一个已存在的模板类实例(比如你的x,类型是A<int>)来初始化新的模板实例时,直接将新实例的模板参数推导为与已有实例完全一致的类型。
它的通用用途主要有三个:
- 对齐普通类的初始化体验:让模板类的
A y = x;这种复制初始化写法和普通类一样自然,不需要开发者手动编写额外的推导指南。 - 消除推导歧义:对于带泛型复制构造函数的模板类,复制推导候选能直接锁定模板参数,避免编译器陷入类型选择的困境。
- 支持聚合类的复制推导:对于没有用户定义构造函数的模板聚合类,复制推导候选也能正确推导模板参数,让复制初始化正常工作。
回到你的例子:#2中,复制推导候选直接推导T=int,随后调用A(const A<int>&)完成y的初始化,这完全符合标准的设计逻辑,也是最合理的推导结果。
内容的提问来源于stack exchange,提问作者ledonter
相关产品推荐
相关产品推荐

