C ++模板选择

时间:2013-03-19 10:31:31

标签: c++ templates c++11

给出以下代码:

#include <memory>
#include <iostream>

using namespace std;

template<typename T>
void test(T & value) {
  cout << "most generic" << endl;
}

template<typename T>
void test(shared_ptr<T> & value) {
  cout << "shared_ptr" << endl;
}

class A {};

int main(int argc, char ** argv) {
  A a;
  shared_ptr<A> p(new A());
  test(a);
  test(p);
  return 0;
}

为什么要打电话

test(p)

使用T = A实例化第二种形式的测试而不是抱怨它无法区分这两种签名?

1 个答案:

答案 0 :(得分:20)

因为尽管它们都是重载决策的可行选择,但第二个功能模板比第一个更专业

根据C ++ 11标准的第13.3.3 / 1段:

  

[...]鉴于这些定义,可行函数F1被定义为更好函数而不是另一个可行函数   F2如果对于所有参数i,ICSi(F1)不是比ICSi(F2)更差的转换序列,然后

     

- 对于某些参数j,ICSj(F1)是比ICSj(F2)更好的转换序列,或者,如果不是,

     

- 上下文是用户定义转换的初始化(见8.5,13.3.1.5和13.3.1.6)和   从返回类型F1到目标类型的标准转换序列(即,类型的   正在初始化的实体)是比标准转换序列更好的转换序列   F2的返回类型到目标类型。 [...]或者,如果不是那样,

     

- F1是非模板函数,F2是函数模板特化,或者,如果不是,

     

- F1和F2是功能模板专精, F1的功能模板更专业   根据14.5.6.2中描述的部分排序规则,而不是F2的模板

§14.5.6.2然后说明如何确定函数模板比​​另一个函数模板更专业。特别是,按照14.5.6.2/2:

  

部分排序通过变换选择两个函数模板中哪一个比另一个更专业   每个模板轮流(参见下一段)并使用该函数执行模板参数推导   类型。演绎过程确定其中一个模板是否比另一个模板更专业。如果   因此,更专业的模板是部分订购流程选择的模板。

标准中的正式定义可能很难破译,但它们的复杂性通常意味着明确地使语言在大多数情况下表现得像我们自然期望的那样

我们对您提供的重载的直观期望是,当参数类型为std::shared_ptr<T>时,应该选择接受std::shared_ptr<int>的那个,因为它似乎正在处理{{1}特别是对象,因此,它看起来比无约束的重载更好(更专业的)候选者。

将这种直观的期望转化为明确的规则集的正式程序可能听起来很复杂,但在我们的情况下,我们想要确定这种过载是不是特别困难:

std::shared_ptr<>

比这更专业:

template<typename T>
void f(std::shared_ptr<T>);

虽然在这种情况下我们很容易根据我们的直觉判断哪一个更专业,但编译器必须依赖算法,并且该算法必须适用于所有情况。

在这种情况下,机制将如下:

  1. 第一个重载,将其模板参数template<typename U> void f(U); 替换为类型参数(任何类型参数),例如T,以及实例化相应的签名 - 函数参数将具有类型int;
  2. 是否始终可以通过提供该类型的对象(在本例中为std::shared_ptr<int>)作为其输入来调用第二重载,并从中推导出类型shared_ptr<int>它?
  3. 嗯,答案是U只会被推断为U;
  4. 现在反过来了:取第二个重载,将其模板参数std::shared_ptr<int>替换为任何类型参数,例如U,并实例化相应的签名 - 函数参数的类型为bool;
  5. 是否始终可以通过提供该类型的对象(bool)作为其参数并从中推导出类型bool来调用第一个重载?
  6. 当然,答案是。无法推断T以使Tstd::shared_ptr<T>匹配;
  7. 结论:第一次重载比第二次更专业。
  8. 当然,当有多个模板参数和多个函数参数时,事情会变得稍微复杂一些,但机制几乎相同。