以下3次重载
template <class T> auto foo() { return 1; }
template <class T> int foo() { return 2; }
template <class T> T foo() { return 3; }
以下是不良形成的?
static_cast<int(*)()>(&foo<int>)();
Clang选择重载#2,而gcc无法编译(Demo)
当删除重载#1时,两者都同意选择重载#2(Demo)。
当删除重载#2时,gcc选择重载#1并且clang无法编译(Demo)
答案 0 :(得分:8)
根据[over.over]/2,我们执行模板参数推导。这将成功实现所有三个重载:在第一个重载中,请记住[temp.deduct.funcaddr]/2:
功能模板的返回类型中的占位符类型(7.1.7.4)是非推断的上下文。如果模板 对于这样的函数,参数推导成功,返回类型是从实例化中确定的 功能体。
由于演绎将成功(假设所有模板参数都明确提供了参数),则返回类型推导为int
。在第二种情况下,从提供参数开始,推论成功,在第三种情况下,推导出T
。 †
继续paragraph 4,
如果选择了多个功能,则任何给定的功能 如果集合包含,则消除函数模板特化
F1
第二个函数模板专业化,其功能模板是 比F1
的功能模板更专业 14.5.6.2的部分排序规则。 此类淘汰后,如果有,将保留一个选定的功能。
根据[temp.deduct.partial]/3,函数模板的函数类型用于部分排序。我们可以立即看到#1和#2的函数类型不包含任何参与演绎的模板参数,因此通过核心问题[temp.deduct.partial]/4的解析引入的1391的添加,它们对应的{{ 1}}不用于确定排序。 @bogdan解释here为什么这个决议有问题;最重要的是,排序只会产生#1和#2的歧义。
也就是说,根据当前(可能是有缺陷的)措辞,转换在所有情况下都是错误的。如果对非依赖/推导参数对修正了部分排序,
† [temp.deduct.type]/8元素9(P
),以防你好奇。