为什么这段C++代码没有产生构造器重定义编译错误?
问题解答
你预期的重定义错误之所以没有出现,核心是第二个构造函数的模板参数处于非推导语境,在你给出的调用场景下根本不会被实例化,自然不会和第一个构造函数产生冲突。
具体推导过程拆解
- 第一个构造函数
template <typename T> Foo(std::initializer_list<T>):
调用Foo foo{ Bar{} }时,初始化列表的元素类型是Bar,编译器可以直接推导出T = Bar,这个构造函数会正常实例化为Foo(std::initializer_list<Bar>),成功被选中调用。 - 第二个构造函数
template <typename T> Foo(std::initializer_list<typename T::BarAlias>):
C++标准明确规定,嵌套在作用域解析符::后面的类型关联的模板参数属于非推导语境,编译器无法通过传入的实参类型反向推导出T的具体取值。比如完全可以有N个不同的类型都定义了using BarAlias = Bar,编译器没有办法确定T应该匹配哪个类型,所以这个构造函数在模板推导阶段就直接被排除,不会参与后续的重载决议,也不会被实例化。
什么情况下会出现你预期的重定义错误
只有你主动强制指定模板参数触发第二个构造函数的实例化时,才会出现重定义报错,比如你添加如下代码:
// 主动指定T=Bar实例化第二个构造函数 template Foo::Foo<Bar>(std::initializer_list<Bar::BarAlias>);
此时两个构造函数实例化后的签名完全一致,编译器才会抛出重定义错误。你的原始代码没有触发这个场景,所以可以正常运行。
内容的提问来源于stack exchange,提问作者mikerru
相关产品推荐
相关产品推荐

