在下面,为什么调用bar
的实例化不是模糊的,而非模板重载函数foo
是不明确的。 nullptr
代替NULL
#include <iostream>
template<typename T>
void bar (T*)
{
std::cout << "bar(T*)" << std::endl;
}
template<typename T>
void bar (typename T::value_type *)
{
std::cout << "bar(T::value_type*)" << std::endl;
}
struct A
{
using value_type = int;
};
void foo (A::value_type*)
{
std::cout << "foo(A::value_type *)" << std::endl;
}
void foo (A*)
{
std::cout << "foo(A *)" << std::endl;
}
int main ()
{
bar<A> (NULL);
foo (NULL); // ambigous
}
编辑:要清楚。我希望foo
重载是模糊的。我不明白为什么在实例化bar
时生成的bar<A>
重载不是同样含糊不清。
答案 0 :(得分:1)
确定哪个超载是最后一个决胜局的最佳可行候选者的规则包括,来自[over.match.best]:
鉴于这些定义,可行函数F1被定义为比另一个可行函数更好的函数 F2如果对于所有参数i,ICSi(F1)不是比ICSi(F2)更差的转换序列,然后是 - [...]
- F1和F2是功能模板专业化,F1的功能模板更专业 根据14.5.6.2中描述的偏序规则,比F2的模板。
只有当我们有两个具有相同转换序列的函数时,才能达到该要点,其中两个函数都是或者都不是函数模板。对于foo
,我们的重载候选者都具有相同的转换序列,也没有函数模板,并且这最后一个要点并不适用 - 所以它是不明确的。
对于bar
,我们可以尝试查看
template<typename T> void bar (T*) // (1)
template<typename T> void bar (typename T::value_type *) // (2)
比其他人更专业。对此的规则基本上是试图看看你是否可以用另一个参数调用一个函数。在这种情况下,任何typename T::value_type*
仍然是指针,因此您可以使用它调用T*
重载。但是,在另一个方向上,模板推断会失败,因为typename T::value_type
是非推断的上下文。因此(2)
被认为更加局部专业化,因此它被选为最佳可行候选者。没有歧义。
答案 1 :(得分:0)
在解决函数模板实例化的函数重载之间存在一个额外的规则:重载函数模板的部分排序( [temp.func.order] 强>)。
这通常用于解决歧义,支持更专业的重载函数模板:
template<typename T> void f(T) { ... }
template<typename T> void f(T*) { ... } // specialization for pointers
void g() { int i; f(&i); } // calls f<int>(int*), not f<int*>(int*)
但是,由于规则的设置方式( [over.match.best] / 1),即使模板参数(此处为T := A
),也会应用功能模板部分排序不是通过模板参数推导从参数推导出来的,即使它是由显式专业化提供的:
[...]
-F1
和F2
是函数模板特化,F1
的函数模板更专业 根据14.5.6.2中描述的部分排序规则,F2
的模板。
此处bar(A::value_type *)
被认为比bar(A *)
更专业(在 [temp.func.order] 下),因此前者是首选,并且没有歧义。