这种模板函数重载的情况使我无法理解

This case of template function overloading eludes my understanding

#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) 至少会导致歧义。这里涉及到什么关于模板函数重载决议的疯狂规则?

一旦我们得到了我们的候选函数集(都是bars),然后将其削减为可行的函数(仍然是bars)我们必须确定best 可行的功能。如果有多个,我们会得到一个歧义错误。 [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) 重载被认为更专业,并被选为最佳可行候选者。


展示了另一个有趣的偏序案例。考虑比较:

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" 重载。