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

This case of template function overloading eludes my understanding

本文关键字:情况 函数 重载      更新时间:2023-10-16
#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 s),然后将其缩减为可行函数(仍然两个bar s),我们必须确定最佳可行函数。如果有多于一个,我们得到一个歧义错误。我们确定最佳人选的步骤见[over.match.best]:

如果对于所有参数i, ICSi(F1)不是比ICSi(F2)更差的转换序列,那么

[A]可行函数F1被定义为比另一个可行函数F2更好的函数,然后
-对于某些参数j, ICSj(F1)是比ICSj(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"重载。
相关文章: