#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)
至少会导致歧义。这里涉及到什么关于模板函数重载解析的疯狂规则?
最佳答案
一旦我们设置了候选函数(都是 bar
),然后将其缩减为可行函数(仍然是 bar
),我们必须确定 最佳 可行函数。如果有多个,我们会得到一个歧义错误。 [over.match.best] 中列出了我们为确定最佳方案而采取的步骤:
[A] viable function F1 is defined to be a better function than another viable function F2 if for all arguments i, ICSi(F1) is not a worse conversion sequence than ICSi(F2), and then
— for some argument j, ICSj(F1) is a better conversion sequence than ICSj(F2), or, if not that,
两个函数都采用 int
类型的参数,因此两个转换序列是相同的。我们继续。
— the context is an initialization by user-defined conversion [...]
不适用。
— the context is an initialization by conversion function for direct reference binding (13.3.1.6) of a reference to function type, [...]
不适用。
— F1 is not a function template specialization and F2 is a function template specialization, or, if not that,
两个 bar<int>
都是函数模板的特化。因此,我们转到最后一个要点来确定最佳可行功能。
— F1 and F2 are function template specializations, and the function template for F1 is more specialized than the template for F2 according to the partial ordering rules described in 14.5.6.2.
部分排序规则基本上归结为我们为 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”重载。
关于c++ - 这种模板函数重载的案例让我无法理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31391172/