函数模板

#include <iostream>

// 多个参数的函数木板
template<typename T1, typename T2>
T2 max(T1 a, T2 b) {

    using namespace std;
    cout << "调用的自定义模板函数...... " << endl;
    return b < a ? a : b;
}

// 显式指定模板参数的类型
template<typename T>
T max1(T a, T b) {
    using namespace std;
    cout << "显式指定模板参数...... " << endl;
    return b < a ? a : b;
}

int main() {
    using namespace std;
    double a = ::max(2, 3.2);
    cout << "max(2, 3.2) = " << a << endl;

    double b = ::max1<double>(2, 4);
    cout << "max1<double>(2, 4) = " << b << endl;

    return 0;
}

函数模板有两种不同类型的参数:

模板参数:在尖括号中声明的

template<typename T>

调用参数:函数模板名称的括号中声明的

T max(T a, T b)

如果使用了多个模板类型参数的话,返回值有时候是个问题,对此有三种方法:

  • 为返回值单独引入一个模板类型参数
  • 让编译器找到返回类型
  • 声明一个其他类型的共同类型

返回值的模板参数

函数模板可以自动检测模板参数类型,因此我们可以不同显式指定,当然显式指定也可以的。

比如:

#include <iostream>

// 显式指定模板参数的类型
template<typename T>
T max1(T a, T b) {
    using namespace std;
    cout << "显式指定模板参数...... " << endl;
    return b < a ? a : b;
}

int main() {
    using namespace std;
    double b = ::max1<double>(2, 4);
    cout << "max1<double>(2, 4) = " << b << endl;
    return 0;
}

在进行 ::max1<double>(2, 4); 调用的时候,就是显式指定了模板参数。

现在来一个为返回值引入第三个模板参数:RT max(T1 a, T2 b);

#include <iostream>

// 多个参数的函数木板
template<typename T1, typename T2, typename RT>
RT max(T1 a, T2 b) {
    using namespace std;
    cout << "调用的自定义模板函数...... " << endl;
    return b < a ? a : b;
}

int main() {
    using namespace std;
    double a = ::max(2, 3.2);
    cout << "max(2, 3.2) = " << a << endl;
    return 0;
}

然后编译的时候就报错了:

报错信息提示:参数 deduction(推理)失败了

这里引出了自动推理的规则:

如果模板参数和调用参数的类型没有什么关系,那么在调用的时候 参数模板的所有类型是不能完全确定的,你必须显式指定模板参数。

因此显式指定以下就可以了:

#include <iostream>

// 多个参数的函数木板
template<typename T1, typename T2, typename RT>
RT max(T1 a, T2 b) {
    using namespace std;
    cout << "调用的自定义模板函数...... " << endl;
    return b < a ? a : b;
}

int main() {
    using namespace std;
    // 模板参数和调用参数不能完全匹配的时候需要显式指定
    double a = ::max<int, double, double>(2, 3.2);
    cout << "max(2, 3.2) = " << a << endl;
    return 0;
}

编译通过,运行成功。

但是指定所有的模板类型又有点啰嗦,这里又来了一条规则:

你必须显式指定直到最后一个不能确定的所有模板类型。

比如上面例子中,RT 的类型是不能通过参数类型推理来确定的,所以导致 T1 和 T2 的类型都要显式指定。

因此这里来了一个骚操作,就是把不能指定的模板类型都放在模板参数列表的最前面,那么后面可以确定的模板参数就可以不用显式指定了。

#include <iostream>

// 多个参数的函数木板
template<typename RT, typename T1, typename T2>
RT max(T1 a, T2 b) {
    using namespace std;
    cout << "调用的自定义模板函数...... " << endl;
    return b < a ? a : b;
}

int main() {
    using namespace std;
    // 这个 double 专门为了 RT 而指定的, T1 和 T2 都可以通过调用参数推导出来
    double a = ::max<double>(2, 3.2);
    cout << "max(2, 3.2) = " << a << endl;
    return 0;
}

我只想说 C++ 是真的骚。

编译器推理返回类型

如果返回类型依赖于模板参数,那么最简单的方法就是让编译器去推导类型。

C++ 14 中已经可以不用指定返回类型了,但是你还是要用 auto 来声明返回类型。

如下面的例子:

#include <iostream>

// 多个参数的函数木板
template<typename T1, typename T2>
auto max(T1 a, T2 b) {
    using namespace std;
    cout << "调用的自定义模板函数...... " << endl;
    return b < a ? a : b;
}

int main() {
    using namespace std;
    // 这个 double 专门为了 RT 而指定的, T1 和 T2 都可以通过调用参数推导出来
    auto a = ::max(2, 3.2);
    auto b = ::max(5, 1.2);
    cout << "max(2, 3.2) = " << a << endl;
    cout << "max(5, 1.2) = " << b << endl;
    return 0;
}

大爷的,我又要去学下 auto 是怎么用的。

使用了 auto 就可以不用 trailing of return type(返回类型后置),但是实际的返回类型还是要通过函数体中的返回语句来推导。

当然了,根据函数体来反推返回值类型要是可行的。

decltype

decay

看到这里有点懵逼。

公共类型作为返回类型

C++ 11 中,标准库提供了一种对多个类型生成共同类型的方法。

例如:

#include <iostream>

// 引入这个玩意儿生成共同类型
#include <type_traits>

// 骚里骚气啊
template<typename T1, typename T2>
std::common_type_t<T1, T2> max(T1 a, T2 b) {
    using namespace std;
    cout << "调用的自定义模板函数...... " << endl;
    return b < a ? a : b;
}

int main() {
    using namespace std;
    // 这个 double 专门为了 RT 而指定的, T1 和 T2 都可以通过调用参数推导出来
    auto a = ::max(2, 3.2);
    auto b = ::max(5, 1.2);
    cout << "max(2, 3.2) = " << a << endl;
    cout << "max(5, 1.2) = " << b << endl;
    return 0;
}

std::common_type 是一种 type trait,能够为返回类型生成一种结构。

typename std::common_type<T1, T2>::type    // since C++ 11

而在 C++ 14 中

std::cmmon_type_t<T1, T2>        // C++ 14 中的写法

服!气!

C++ 函数模板的返回类型如何确定?的更多相关文章

  1. C++函数模板

    函数模板提供了一种函数行为,该函数行为可以用多种不同的类型进行调用,也就是说,函数模板代表一个函数家族,这些函数的元素是未定的,在使用的时候被参数化. 本文地址:http://www.cnblogs. ...

  2. C++ 初识函数模板

    1. 前言 什么是函数模板? 理解什么是函数模板,须先搞清楚为什么需要函数模板. 如果现在有一个需求,要求编写一个求 2 个数字中最小数字的函数,这 2 个数字可以是 int类型,可以是 float ...

  3. C++函数重载和函数模板

    1.函数重载 这是小菜鸟写的一个例子. 函数重载应该注意以下几点: 1.1重载函数有类似的功能: 1.2只能以参数的类型(形参个数和类型)来重载函数, int max(int a,int b);flo ...

  4. C++_进阶之函数模板_类模板

     C++_进阶之函数模板_类模板 第一部分 前言 c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定,用一个虚拟的类型来 ...

  5. C++复习:函数模板和类模板

    前言 C++提供了函数模板(function template).所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体指定,用一个虚拟的类型来代表.这个通用函数就称为函数模板.凡是函数体 ...

  6. C++解析(26):函数模板与类模板

    0.目录 1.函数模板 1.1 函数模板与泛型编程 1.2 多参数函数模板 1.3 函数重载遇上函数模板 2.类模板 2.1 类模板 2.2 多参数类模板与特化 2.3 特化的深度分析 3.小结 1. ...

  7. [转]C++函数模板与模板函数

      1.函数模板的声明和模板函数的生成   1.1函数模板的声明 函数模板可以用来创建一个通用的函数,以支持多种不同的形参,避免重载函数的函数体重复设计.它的最大特点是把函数使用的数据类型作为参数. ...

  8. C++模板之函数模板实例化和具体化

    模板声明 template<typename/class T>,  typename比class最近后添加到C++标准. 常规模板,具体化模板,非模板函数的优先调用顺序. 非模板函数(普通 ...

  9. C++泛型编程之函数模板

    泛型语义 泛型(Generic Programming),即是指具有在多种数据类型上皆可操作的含意.泛型编程的代表作品 STL 是一种高效.泛型.可交互操作的软件组件. 泛型编程最初诞生于 C++中, ...

随机推荐

  1. 2019-1-23IntelliJ IDEAget的使用教程及出现的问题

    第一条:快捷键: Ctrl+Alt+h:显示调用当前方法的所有位置. Ctrl+Alt+B:跳转到方法实现处 自动修正,我这是 Alt + L Ctrl+Enter,导入包,自动修正Ctrl+Alt+ ...

  2. LOJ.6074.[2017山东一轮集训Day6]子序列(DP 矩阵乘法)

    题目链接 参考yww的题解.本来不想写来但是他有一些笔误...而且有些地方不太一样就写篇好了. 不知不觉怎么写了这么多... 另外还是有莫队做法的...(虽然可能卡不过) \(60\)分的\(O(n^ ...

  3. pip安装django失败

    pip install django时提示 Cannot fetch index base URL https://mirrors.tuna.tsinghua.edu.cn/pypi/simple/, ...

  4. Python3从零开始爬取今日头条的新闻【三、滚动到底自动加载】

    Python3从零开始爬取今日头条的新闻[一.开发环境搭建] Python3从零开始爬取今日头条的新闻[二.首页热点新闻抓取] Python3从零开始爬取今日头条的新闻[三.滚动到底自动加载] Pyt ...

  5. BZOJ4039 : 集会

    将曼哈顿距离转化为切比雪夫距离,即: $|x_1-x_2|+|y_1-y_2|=\max(|(x_1+y_1)-(x_2+y_2)|,|(x_1-y_1)-(x_2-y_2)|)$ 那么每个点能接受的 ...

  6. JS 模仿块级作用域

    function outputNumbers(count) { for (var i=0; i<count; i++) { console.log(i); } var i;  // 重新声明变量 ...

  7. 批量操作数据库数据mybatis.xml

    批量插入数据 <insert id="equipment_Add" parameterType="cn.wtsr.core.web.dao.vo.equipment ...

  8. Java 多线程 ReadWriteLock

    ReadWriteLock是JDK 1.5提供的读写分离锁,可以减少锁竞争.例如,线程A1.A2和A3进行写操作,线程B1.B2和B3进行读操作,如果使用重入锁或者内部锁,那么理论上所有读之间.读与写 ...

  9. 超级牛皮的oracle的分析函数over(Partition by...) 及开窗函数 (转)

    http://zonghl8006.blog.163.com/blog/static/4528311520083995931317/ over(Partition by...) 一个超级牛皮的ORAC ...

  10. IDEA 下载 和 安装

    1. IDEA 下载 网址     pttps://www.jetbrains.com IDEA      优点  :高度集成企业软件工程的概念(svn, git) 缺点: 破解存在在法律风险 ; E ...