#include <iostream>
template<typename T>
struct identity
{
typedef T type;
};
template<typename T> void bar(T) { std::cout << "a" << std::endl; }
template<typename T> void bar(typename identity<T>::type) { std::cout << "b" << std::endl; }
int main ()
{
bar(5); // prints "a" because of template deduction rules
bar<int>(5); // prints "b" because of ...?
return EXIT_SUCCESS;
}
我预计bar<int>(5)
至少会产生歧义。这里涉及到关于模板函数重载决策的疯狂规则?
答案 0 :(得分:17)
一旦我们设置了候选函数(两个bar
s),然后将其缩小到可行函数(仍然是bar
s),我们必须确定 best 可行的功能。如果有多个,我们会出现歧义错误。我们采取的确定最佳方法的步骤在[over.match.best]中列出:
[A]可行函数F1被定义为比其他可行函数F2更好的函数如果对于所有参数 i ,ICS i (F1)并不是更糟糕转换序列比ICS i (F2),然后是 - 对于某些参数 j ,ICS j (F1)是比ICS j (F2)更好的转换序列,或者,如果不是,
两个函数都采用int
类型的参数,因此两个转换序列都是相同的。我们继续。
- 上下文是用户定义的转换初始化[...]
不适用。
- 上下文是转换函数的初始化,用于对函数类型的引用的直接引用绑定(13.3.1.6),[...]
不适用。
- F1不是功能模板专业化,F2是功能模板专业化,或者,如果不是,
两个bar<int>
都是函数模板特化。因此,我们进入最后一个要点,以确定最佳可行功能。
- F1和F2是功能模板专精,F1的功能模板更专业 根据14.5.6.2中描述的偏序规则,比F2的模板。
部分排序规则基本上归结为我们为bar
重载的参数合成新的唯一类型,并在另一个重载上执行模板推导。
首先考虑“b”过载。合成类型typename identity<Unique1>::type
并尝试对T
执行模板推导。这成功了。最简单的模板演绎有。
接下来,考虑“a”过载。合成类型Unique2
并尝试对typename identity<T>::type
执行模板推导。这个失败!这是一个非推断的上下文 - 没有扣除可以成功。
由于模板类型推导仅在单一方向上成功,因此bar(typename identity<T>::type)
重载被认为更加专业化,并被选为最佳可行候选者。
bogdan提出了另一个有趣的案例,用于查看部分排序。请考虑比较:
template <typename T> void bar(T, T); // "c"
template <typename T> void bar(T, typename identity<T>::type ); // "d"
bar(5,5);
bar<int>(5, 5);
同样,两个候选人都是可行的(这次甚至没有明确指定T
),所以我们看看部分排序规则。
对于“c”重载,我们合成类型为UniqueC, UniqueC
的参数,并尝试对T, typename identity<T>::type
进行推导。这成功(使用T == UniqueC
)。所以“c”至少和“d”一样专业。
对于“d”重载,我们合成类型为UniqueD, typename identity<UniqueD>::type
的参数,并尝试对T, T
进行推导。这失败了!参数是不同类型的!所以“d”至少不如“c”那么专业。
因此,调用“c”重载。