2016-09-06 63 views
8

我无法理解为什么下列导致暧昧电话:重载决策与模板参数

#include <iostream> 

// generic version f(X, Y) 
template <class X, class Y> 
void f(X x, Y y) { 
    std::cout << "generic" << std::endl; 
} 

// overload version 
template <class X> 
void f(X x, typename X::type y) { 
    std::cout << "overload" << std::endl; 
} 

struct MyClass { 
    using type = int; 
}; 

int main() { 
    f(MyClass(), int()); // Call to f is ambiguous 
} 

我期望过载版本,这是更加专业化比普通版的第二个参数,以被选为最佳人选。我知道,如果我改变过载版本

template <class X> 
void f(X x, int y) { 
    std::cout << "overload" << std::endl; 
} 

则呼叫解决就好了,这意味着它有一个事实,即X ::类型是依赖模板的名称,但仍然不能工作了做为什么失败。任何帮助深表感谢。

回答

8

首先,我们选择可行的候选人。这些是:

void f(MyClass, int);     // with X=MyClass, Y=int 
void f(MyClass, typename MyClass::type); // with X=MyClass 

那些候选人都采用相同的参数,所以有等效的转换序列。因此,没有基于这些同分决赛的适用,所以我们退回到[over.match.best]最后可能决胜局:

根据这些定义,一个可行的函数F1被定义为比一个更好的功能另一个可行的功能 F2如果对于所有参数i,ICSi(F1)不是比ICSi(F2)更差的转换序列,然后F1和F2是功能模板专业化,并且F1的功能模板更多根据14.5.6.2中描述的偏序排列规则,与F2的模板相比专用 。

因此,我们尝试基于部分排序规则对两个函数模板进行排序,这些模板涉及为每个模板参数合成一个唯一类型,并尝试针对每个超载执行模板推演。但是,随着从[temp.deduct.partial]一个键相关的附加规则:

每种类型从参数模板和从参数 模板相应类型以上提名被用作类型P和A. 的如果一个特定的P不包含在模板参数推导中参与 的模板参数,那么P不用于确定排序。

那么这是什么意思。首先,我们尝试从过载推导出通用版本。我们选择合成类型UniqueX(对于X)和UniqueX_type(对于typename X::type)并查看我们是否可以调用通用函数。这成功了(与X=UniqueXY=typename X::type)。

让我们来试一下。我们选择UniqueX(对于X)和UniqueY(对于Y)并尝试执行模板推演。对于第一个P/A对来说,这个小小的成功。但对于第二个参数,X是一个非推导的上下文,你会认为这意味着模板扣除失败。 但是按照报价的粗体部分,我们只是跳过这个P/A对用于订购。所以,由于第一个P/A对成功,我们认为整个扣除过程已经成功。

由于模板扣除在两个方向都成功,我们不能选择一个功能或另一个功能更专业化。由于没有进一步的平局,所以没有一个最好的可行候选人,所以这个呼叫是不明确的。


当第二过载被改变为:

template <class X> void f(X, int); 

改变是现在扣在一个方向上发生故障的处理的一部分。我们可以推导出X=UniqueX,但第二对具有类型为int的参数和类型为UniqueY的参数,这将不起作用,所以此方向失败。在相反的方向,我们可以推导出X=UniqueXY=int。这使得这个过载更加专业化,以至于泛型超载,所以它最好是我最初提到的最后一个tiebreaker。


作为附录,请注意,模板的部分排序很复杂。考虑:

template <class T> struct identity { using type = T; }; 

template <class T> void foo(T);        // #1 
template <class T> void foo(typename identity<T>::type); // #2 

template <class T> void bar(T, T);       // #3 
template <class T> void bar(T, typename identity<T>::type); // #4 

foo(0);  // calls #1, #2 isn't even viable 
foo<int>(0); // calls #2 
bar(0,0); // calls #3! we fail to deduce 3 from 4, but we succeed 
      // in deducing 4 from 3 because we ignore the second P/A pair! 
+0

我打算写相同的答案,但卡住了导致你强调**但**在你的解释中的部分。我仍然无法消化你的答案中的第二个引号。然而,我相信这应该是被接受的答案。 – Leon

+0

@Leon最初选错了报价,我的错。新的是我想要包括的那个。 – Barry