我有以下程序:
#include <iostream>
namespace detail {
template <class T> char test(int T::*)
{
std::cout << "Came to one\n";
return 0;
}
template <class T> int test(...)
{
std::cout << "Came to two\n";
return 0;
}
}
struct A {};
int main()
{
detail::test<A>(0);
detail::test<int>(0);
}
使用g ++ 4.8.2进行测试时,会产生以下输出:
Came to one Came to two
我的问题:为什么第一次调用会明确选择detail::test
的第一个版本?
更新
如果没有details::test
的第一个版本,main
的代码编译得很好。当它存在时,编译器认为它比detail::test<A>()
更好地匹配第二个。
更新2
在阅读之后,即使对于不完整的类型或没有指定类型的成员,指向成员的指针也是格式正确的。,我尝试了以下内容并且它可以正常工作。
struct A;
int main()
{
detail::test<A>(0);
detail::test<int>(0);
}
C ++ 11标准有很多地方可以发现我不会想到的概念。
答案 0 :(得分:10)
编译器经历名称查找,参数推导和过载解析的三位一体。名称查找为test
找到两个重载,并且对于非类类型,对成员的指针的参数推断将失败,但对于不完整类型或缺少成员则不会。最后,在可行的候选者中,重载决策选择最佳匹配(省略号转换是最低级别)。
这里有三个相关的标准引用:
根据 8.3.3指向成员的指针[dcl.mptr] / 2 中的示例,即使对于不完整类型或没有指定类型的成员,指向成员的指针也是格式良好的。
根据 14.8.2模板参数演绎[temp.deduct] / 8 :
如果替换导致无效的类型或表达式,请键入 扣除失败。
列出的众多例子之一是:
当T不是类时,尝试创建“指向T成员的指针” 类型。
最后,根据 13.3.3.2对隐式转换序列进行排名[over.ics.rank] ,省略号(...)
重载在重载解析期间具有所有隐式转换序列的最低等级: / p>
2比较隐式转换序列的基本形式(如 在13.3.3.1中定义
- 标准转换序列(13.3.3.1.1)是 比用户定义的转换序列更好的转换序列 或省略号转换序列,
- 用户定义的转化 序列(13.3.3.1.2)是比省略号更好的转换序列 转换顺序(13.3.3.1.3)。
你的第一个电话detail::test<A>(0);
有两个可行的候选人,但它选择了第一个超载,因为它是一个更好的匹配。第二次调用detail::test<int>(0);
在第一次重载时给出了一个替换错误,因此选择了第二次匹配。
答案 1 :(得分:2)
在这个例子中,第一个重载在第二个上被选中,因为就parameter conversion sequences而言,它更适合 - 省略号转换序列排在最后。