0
点赞
收藏
分享

微信扫一扫

001-EMC 深入解读-理解模板型别推导(一)


EMC 第一章条款一,一上来就出个王炸——有关于函数模板的推导规则。

1. 问题

似乎你在使用的时候并未关注过这些事情,因为一切看起来相当自然,你也用的很爽。比如:

template <typename T>
T add(T a, T b) {
return

这是一个计算两数之和的函数,你可以传递 float, double, int 等支持 operator+ 类型的参数,而不用为每一种类型都写一份 add 函数。

但是,上面的函数模板你也可以写成:

template <typename T>
T add(T& a, T& b) {
return a + b;
}

template <typename T>
T add(const T& a, const T& b) {
return a + b;
}

template <typename T>
T add(T&& a, T&& b) {
return

然而,上面这 3 种写法在 C++11 中都好使。到底哪种比较好?或者说,有什么区别?从直觉上来说,写在 add(T a, T b) 是值传递,而带 & 的则是引用传递,带 const 则表示希望对象保持常量属性。你的直觉是对的。

那么 T&& 是什么?这是 C++11 出现的特有语法,你也许听说过右值引用,很抱歉,看起来 T&& 确实像右值引用,不过,当 && 出现在模板参数 T 后面时,则称为 Universal Reference(通用引用、万能引用)。接下来我们给一个比较正式的定义:

函数模板中持有型别形参 T 时,Universal Reference 写作 T&&

现在将问题抽象一下,对于下面的模板:

template<typename T>
void

问题:在不同的模板参数里,传递不同类型的参数,T 会被编译器推导为什么?ParamType 又是什么?

例如:

template<typename T>
void f(const T& param) // ParamType = const T&

int x = 27;
const int cx = x;
const int& rx = x;

当你调用 ​​f(x)​​​, ​​f(cx)​​​, ​​f(rx)​​ 时,T 是什么,ParamType 是什么?

事实上,上面的 3 种情况,T 全部被推导为 ​​int​​​,所以,ParamType = ​​const T&​​​ = ​​const int&​​.

如果模板是:​​void f(T& param)​​,情况又是怎样呢?如何在你的编译器中验证?

2. 实验

我们没一个比较好的直接的方法在程序里打印出推导的类型,但是可以利用链接器的错误提示来看到实际推导类型。

2.1 void f(T& param)

编译以下代码,命名为 deduce01.cpp

  • 推导 f(x)

// deduce01.cpp
template <typename T>
void f(T& param); // 故意不写 f 的定义

int main() {
int x = 27;
const int cx = x;
const int& rx = x;

f(x);
// f(cx);
// f(rx);
return 0;
}

使用下面方式编译:

$ g++ -std=c++11 deduce01.cpp


001-EMC 深入解读-理解模板型别推导(一)_emc


图1 调用 f(x)


可以看到,f(x) 里,T 被推导为 int,而 ParamType 被推导为 int &.

  • 推导 f(cx)

如果调用 f(cx),再进行编译:

// deduce.01.cpp
template <typename T>
void f(T& param); // 故意不写 f 的定义

int main() {
int x = 27;
const int cx = x;
const int& rx = x;

// f(x);
f(cx);
// f(rx);
return 0;
}


001-EMC 深入解读-理解模板型别推导(一)_effective modern c++_02


图2 调用 f(cx)


可以看到,调用 f(cx) 时,T 被推导为 int const, ParamType 被推导为 int const&.

  • 推导 f(rx)

如果调用 f(rx),再进行编译:

// deduce.01.cpp
template <typename T>
void f(T& param); // 故意不写 f 的定义

int main() {
int x = 27;
const int cx = x;
const int& rx = x;

// f(x);
// f(cx);
f(rx);
return 0;
}


001-EMC 深入解读-理解模板型别推导(一)_c++11_03


图3 调用 f(rx)


推导的类型和调用 f(cx) 一模一样。

2.2 void f(const T& param)

同样的,我们编译 deduce02.cpp,使用上述方法编译:

// deduce02.cpp
template <typename T>
void f(const T& param); // 故意不写 f 的定义

int main() {
int x = 27;
const int cx = x;
const int& rx = x;

// 这里一次调用 3 次 f
f(x);
f(cx);
f(rx);
return 0;
}


001-EMC 深入解读-理解模板型别推导(一)_函数模板_04


图4 调用 f(x), f(cx), f(rx)


你发现,只报了一个链接错误,这意味着这 3 次调用,推导的结果是一样的。T 被推导为 int,而 ParamType 被推导为 int const&.

3. 总结

  • 掌握查看推导类型的方法

后文我们继续讨论具体的规则:

  • ParamType 是引用或指针,但不是通用引用
  • ParamType 是通用引用
  • ParamType 既非指针也非引用


举报

相关推荐

0 条评论