C++模板函数

类型推断

        模板在编译过程中,会进行类型推断,平时使用到隐式类型转换(自动类型转换),在类型推断时,几乎全部失效。经常用到的隐式类型转换包含以下几种:

  • 从低精度类型到高精度类型的转换,如char->int,int->double
  • 从数组到指针,如char []->char *
  • 通过构造函数进行类型转换,例如char *->std::string,std::string str = "hello"
  • 通过继承关系,子类对象引用自动转成基类对象引用,如B派生自A,对于函数A &max5(A &x, A &y), 定义对象 A a和对象B b,max(a, b)这样的调用是没有问题的

        下面是一段测试代码:

#include <cstdio>
#include <string>template <typename T>
T max1(T x, T y)
{printf("x : %s, y : %s\n", typeid(x).name(), typeid(y).name());return x > y ? x : y;
}template <typename T>
T &max2(T &x,  T &y)
{printf("x : %s, y : %s\n", typeid(x).name(), typeid(y).name());return x > y ? x : y;
}template <typename T>
const T &max3(const T &x,  const T &y)
{printf("x : %s, y : %s\n", typeid(x).name(), typeid(y).name());return x > y ? x : y;
}class A
{
public:A(int i) : m_data(i) {}bool operator> (const A &rhs) const {printf("A\n");return m_data > rhs.m_data;}protected:int m_data;
};class B : public A
{
public:B(int i) : A(i) {}bool operator> (const B &rhs) const {printf("B\n");return m_data > rhs.m_data;}};int main(int argc, char **argv)
{char c0 = 'a';int i0 = 10;int *i0p = &i0;const int i1 = 14;double d0 = 3.14;char *s0 = "100";char s1[8] = "hello";const char *s2 = "world";std::string str0 = "200";A a0(10);B b0(12);A &a1 = a1;A &a2 = b0;max1(c0, i0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char' vs. 'int')max1(i0, d0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'double')max1(i0, i1); //int max1(int, int) 无法确认是否有const修饰符max1(s0, s1); //char *max1(char *, char *)max1(s0, str0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char *' vs. 'std::string' (aka 'basic_string<char>'))max1(s0, s2); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char *' vs. 'const char *')max1(a0, b0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('A' vs. 'B')max1(a1, a2); //A max1(A, A)max1(a0, a2); //A max1(A, A)max1(a0, b0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('A' vs. 'B')max2(c0, i0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char' vs. 'int')max2(i0, d0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'double')max2(i0, i1); //Candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'const int')max2(i0, i1); //Candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'const int')max2(s0, s1); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char *' vs. 'char[8]')max2(s0, str0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char *' vs. 'std::string' (aka 'basic_string<char>'))max2(s0, s2); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char *' vs. 'const char *')max2(a0, b0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('A' vs. 'B')max2(a1, a2); //A max2(A, A)max2(a0, a2); //A max2(A, A)max2(a0, b0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('A' vs. 'B')max3(c0, i0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char' vs. 'int')max3(i0, d0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'double')max3(i0, i1); //int max3(int, int) 无法确认是否有const修饰符max3(s0, s1); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char *' vs. 'char[8]')max3(s0, str0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char *' vs. 'std::string' (aka 'basic_string<char>'))max3(s0, s2); //Candidate template ignored: deduced conflicting types for parameter 'T' ('char *' vs. 'const char *')max3(a0, b0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('A' vs. 'B')max3(a1, a2); //A max3(A, A)max3(a0, a2); //A max3(A, A)max3(a0, b0); //Candidate template ignored: deduced conflicting types for parameter 'T' ('A' vs. 'B')}

        通过上面的代码可以看出,模板使用非const引用传参是对变量类型要求最严格的,不允许任何隐式类型转换;模板使用const引用传参允许const类型和非const类型转换;模板使用值传参支持数组到同类型指针的转换,也支持const类型和非const类型的转换。关于const类型和非const类型转换,从代码看不出到底是const类型转换成了非const类型,还是非const类型转换成了const类型,但可以从最终的二进制文件看出max1是把const int转换成了int,max3是吧int转换成了const int,如下:

        如果确实要使用到不同的类型,有3中处理方案:

  • 强制类型转换
max1(static_cast<double>(3), 3.14);
  • 显示指出T的类型,告诉编译器不做类型推导
max1<double>(3, 3.14);
  • 使用多个模板参数,指明参数类型有可能不相同

多模板参数

        多模板参数很好理解,但在某些情况下,如上文中的max函数,面临一个问题——如何选择返回值类型?为什么这个问题很重要,先来看一个例子,如下:

#include <cstdio>template <typename T1, typename T2>
T1 max1(T1 x,  T2 y)
{return x > y ? x : y;
}int main(int argc, char **argv)
{auto temp1 = max1(4.14, 3); //temp1 = 4.14auto temp2 = max1(3, 4.14); //temp2 = 4return 0;
}

        看到这样的结果,是不是感到很困惑?为什么会有这样的结果?从max1的定义可以看出,max1返回值的类型是第一个参数的类型。尽管两次调用的参数都是4.14和3,但第一次调用,返回值类型T1和4.4相同,为double,所以返回值为4.14;第二次调用返回值类型T1和3相同,为int,尽管计算结果和第一次相同为4.14,但返回时被转换int类型,变为4.

        基于上面的问题,必须认真对待返回值的类型。关于上述问题,有三种解决方案:

  • 专门为返回值声明一个模板类型
#include <cstdio>template <typename T1, typename T2, typename RT>
RT max2(T1 x, T2 y) {return x > y ? x : y;
}int main(int argc, char **argv)
{auto temp1 = max2(4.14, 3); //temp1 = 4.14auto temp2 = max2(3, 4.14); //temp2 = 4return 0;
}

        上面的源码就可以运行了吗?(⊙o⊙)…编译失败了

Candidate template ignored: couldn't infer template argument 'RT'
Candidate template ignored: couldn't infer template argument 'RT'

        原因是编译器仅对函数参数的类型进行推导,RT不是任何参数的类型,所以不会被推导。必须使用下面的方法调用:

    auto temp1 = max2<double, int, double>(4.14, 3);auto temp2 = max2<int, double, double>(3, 4.14);

         是不是很繁琐?一个不小心,又会翻船😄

  • 返回值类型声明为auto,让编译器推导返回类型
#include <cstdio>template <typename T1, typename T2>
auto max3(T1 x, T2 y) {return x > y ? x : y;
}int main(int argc, char **argv)
{auto temp1 = max3(4.14, 3);auto temp2 = max3(3, 4.14);return 0;
}

        上述测试代码仅对c++14,及以后版本生效。对于c++11,上述源码会报错:

 error: 'auto' return without trailing return type; deduced return types are a C++14 extension

        关于上述错误,可以使用decltype关键字对返回类型进行显示推导:

template <typename T1, typename T2>
auto max4(T1 x, T2 y) -> decltype(true ? x : y) {return x > y ? x : y;
}

        注意,decltype操作符仅仅推导传入语句运算结果的的类型,不关心最终结果,因此,还可以如下实现:

template <typename T1, typename T2>
auto max4(T1 x, T2 y) -> decltype(x - y) {return x > y ? x : y;
}

        再换种实现,看下是不是很想lambda表达式?

template <typename T1, typename T2>
auto max4(T1 x, T2 y) -> double {return x > y ? x : y;
}

 

  • 将返回值设置为公共类型
template <typename T1, typename T2>
std::common_type_t<T1 , T2> max5(T1 x, T2 y) {return x > y ? x : y;
}

        注意,此时声明中不能出现任何引用的痕迹,std::common_type_t<T1 &, T2 &> max5(T1 &x, T2 &y) ,或std::common_type_t<T1 , T2> max5(T1 &x, T2 &y)都是错误的方式。

Candidate function [with T1 = double, T2 = int] not viable: expects an lvalue for 1st argument

        对于c++11,common_type_t的使用方式和此处有点不一样,下面是c++11的实现: 

template <typename T1, typename T2>
typename std::common_type<T1 , T2>::type max6(T1 x, T2 y) {return x > y ? x : y;
}

 默认模板参数

        标题中的概念很好理解,就是给模板参数指定一个默认值。对于模板参数的默认值是直接指定,还是通过计算推导得出,编译器是不关心的,因此下面几种实现都是可以的:

#include <cstdio>
#include <type_traits>template <typename T1, typename T2, typename RT=double>
RT max1(T1 x, T2 y) {return x > y ? x : y;
}template <typename T1, typename T2, typename RT=std::decay_t<decltype(true ? T1() : T2())>>
RT max2(T1 x, T2 y) {return x > y ? x : y;
}template <typename T1, typename T2, typename RT=std::common_type_t<T1, T2>>
RT max3(T1 x, T2 y) {return x > y ? x : y;
}int main(int argc, char **argv)
{auto v1 = max1(7.98, 10);auto v2 = max2(7.98, 10);auto v3 = max3(7.98, 10);return 0;
}

模板函数重载

        模板函数重载比普通函数重载更为复杂,模板参数的个数不同也可以算作重载,即使其中的某些模板参数不使用。但是,不要试图通过不同的模板参数名称去实现模板函数的重载,这样只会有两种可能:重复的函数定义;模板实例不知道调用哪个实现。

        一个非模板函数可以和一个与其同名的函数模板共存,并且这个同名的函数模板可以被实例化为与非模板函数具有相同类型的调用参数。在所有其它因素都相同的情况 下,模板解析过程将优先选择非模板函数,而不是从模板实例化出来的函数。下面是测试代码:

#include <cstdio>
#include <cstring>template <typename T>
T max1(T x, T y) {return x > y ? x : y;
}// Redefinition of 'max1'
/*template <typename U>
U max1(U x, U y) {return x > y ? x : y;
}*/template <typename T, typename RT>
T max1(T x, T y) {return x > y ? x : y;
}template <typename T1, typename T2, typename RT=decltype(true ? T1() : T2())>
RT max1(T1 x, T2 y) {return x > y ? x : y;
}//如果存在下面的定义,m1将会不知道调用RT max1(T2 x, T1 y),还是RT max1(T1 x, T2 y)
/*template <typename T1, typename T2, typename RT=decltype(true ? T1() : T2())>
RT max1(T2 x, T1 y) {return x > y ? x : y;
}*///template<>,打开此处注释,m6会调用该函数
const char *max1(const char *s1, const char *s2) {return strcmp(s1, s2) > 0 ? s1 : s2;
}int main(int argc, char **argv)
{auto m1 = max1(100, 4.8); //RT max1(T1 x, T2 y)auto m2 = max1("abc", "efd"); //const char *max1(const char *s1, const char *s2)//auto m3 = max1("abc", 10000); //Candidate template ignored: substitution failure [with T1 = const char *, T2 = int]: incompatible operand types ('const char *' and 'int')auto m4 = max1(10, 245); //T max1(T x, T y)auto m5 = max1<int, int>(10, 245); //template <typename T, typename RT> T max1(T x, T y)auto m6 = max1<>("abc", "efd"); //T max1(T x, T y),强制使用模板函数return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/656564.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IDEA新建项目,但是Spring Initializr最低17

目录 问题解决 问题 在IDEA中新建项目&#xff0c;使用Spring Initializr的方式&#xff0c;但是java版本只有17和21 但是它高任它高&#xff0c;我只搞Java8 解决 替换源&#xff0c;即更换Server URL&#xff0c;改为https://start.aliyun.com 然后就可以用Java8 了

CTFHub:web-LD_PRELOAD-WP

解题思路 思路分析 根据资料可得知有四种绕过 disable_functions 的手法&#xff1a; 攻击后端组件&#xff0c;寻找存在命令注入的 web 应用常用的后端组件&#xff0c;如&#xff0c;ImageMagick 的魔图漏洞、bash 的破壳漏洞等等寻找未禁用的漏网函数&#xff0c;常见的执…

【数据结构】(二)线性表List

目录 1、基本概念 2、栈&#xff08;Stack&#xff09; 3、队列&#xff08;Queue&#xff09; 4、串&#xff08;String&#xff09; 1、基本概念 &#xff08;1&#xff09;线性表是零或多个数据元素的有限序列。 &#xff08;2&#xff09;数组长度指存储空间长度&…

Kotlin快速入门系列6

Kotlin的接口与扩展 接口 与Java类似&#xff0c;Kotlin使用interface关键字定义接口&#xff0c;同时允许方法有默认实现&#xff1a; interface KtInterfaceTest {fun method()fun methodGo(){println("上面方法未实现&#xff0c;此方法已实现")} } 接口实现 …

Codeforces Round 898 (Div. 4)

目录 A. Short Sort B. Good Kid C. Target Practice D. 1D Eraser E. Building an Aquarium F. Money Trees G. ABBC or BACB H. Mad City A. Short Sort 这种判断是否能变成目标串的我们都是通过一定手段然后看最后是否是直接变成目标串即可 void solve(){string s; …

JS第一课简单看看这是啥东西

1.什么是JavaScript JS是一门编程语言&#xff0c;是一种运行在客户端(浏览器)的编程语言&#xff0c;主要是让前端的画面动起来&#xff0c;注意HTML和CSS不是编程语言&#xff0c;他俩是一种标记语言。JS只要有浏览器就能运行不用跟Python或者Java一样上来装一个jdk或者Pyth…

12306提示人证核验失败问题解决方案

问题环境&#xff1a;手机已经 Root 并且安装了其他软件 认证时提示 官方客服回复: 可能是我的人脸发生了太大变化导致&#xff0c;建议我去身份证的公安部门更新人脸信息&#xff0c;但是想一想又不对&#xff0c;如果发生了大变化所有 App 使用的都是统一的公安部的人脸信息…

探索数字经济:从基础到前沿的奇妙旅程

新一轮技术革命方兴未艾&#xff0c;特别是以人工智能、大数据、物联网等为代表的数字技术革命&#xff0c;催生了一系列新技术、新产业、新模式&#xff0c;深刻改变着世界经济面貌。数字经济已成为重组全球要素资源、重塑全球经济结构、改变全球竞争格局的关键力量。预估到20…

关于maven项目构建的解释

在Idea中使用模块化构建项目 项目介绍&#xff1a; sky-server依赖sky-pojo和sky-common&#xff0c;继承sky-take-outsky-pojo继承sky-take-outsky-common继承sky-take-out 由于Idea编译器自动识别引入的模块&#xff0c;所以在Idea中可以运行项目。 在Idea中使用maven打包…

Redis -- 背景知识

目录 特性 为啥Redis快? 应用场景 Redis不能做什么&#xff1f; Redis是在内存中存储数据的一个中间件&#xff0c;用作为数据库&#xff0c;也可以用作为缓存&#xff0c;在分布式中有很高的威望。 特性 In-memory data structures&#xff1a;在内存中存储数据key-val…

设计模式——职责链模式(Chain of Responsibility Pattern)

概述 职责链模式(Chain of Responsibility Pattern)&#xff1a;避免请求发送者与接收者耦合在一起&#xff0c;让多个对象都有可能接收请求&#xff0c;将这些对象连接成一条链&#xff0c;并且沿着这条链传递请求&#xff0c;直到有对象处理它为止。职责链模式是一种对象行为…

少儿编程 中国电子学会图形化编程2021年3月等级考试Scratch三级真题解析(选择题、判断题)

1.在《采矿》游戏中&#xff0c;当角色捡到黄金时财富值加1分&#xff0c;捡到钻石时财富值加2分&#xff0c;下面哪个程序实现这个功能&#xff1f; A&#xff1a; B&#xff1a; C&#xff1a; D&#xff1a; 2.设计一个和在20以内&#xff08;包括20&#xff09;的整数加法…

通过docker构建基于LNMP的WordPress项目

计划通过自定义网络模式&#xff0c;创建一个172.18.0.0/16网段 nginx&#xff1a;172.18.0.2:80 php&#xff1a;172.18.0.3:9000 mysql&#xff1a;172.18.0.4:3306 创建nginx的镜像 准备好nginx的安装包 准备nginx的网页目录和wordpress网站目录以及nginx.conf文件 编…

前段,关于Javascript的学习,数据类型

<html> <head> <title>Javascript Traning</title> <script> //注意Javascript里边的变量类型都用 var开头 var value "abc";//字符串类型 alert(value); var value110; var value210;//数字类型 alert(value2); //数字相加 aler…

中国地区cetos7.9 install kubeadmin

第 1 步&#xff1a;禁用 SELinux&#xff08;可选但推荐&#xff09; 如何在 CentOS 7 上查找 SELinux 状态 sestatus另一种选择是运行以下 cat 命令&#xff1a; vi /etc/selinux/config SELINUXdisabled rebootcentos7 linux 安装k8s前下面操作的作用是&#xff1f; cat…

用Python库pillow处理图像

入门知识 颜色。如果你有使用颜料画画的经历&#xff0c;那么一定知道混合红、黄、蓝三种颜料可以得到其他的颜色&#xff0c;事实上这三种颜色就是美术中的三原色&#xff0c;它们是不能再分解的基本颜色。在计算机中&#xff0c;我们可以将红、绿、蓝三种色光以不同的比例叠加…

腾讯云云监控实践:使用云审计 CloudAudit SDK 精准管理腾讯云资源

文章目录 一、什么是腾讯云的操作审计 CloudAudit二、CloudAudit 有哪些优势三、CloudAudit 应用场景举例3.1 安全分析3.2 资源变更跟踪3.3 合规性审计 四、使用云审计 SDK 进行云监控4.1 安装环境包 PHP4.2 下载并解压云审计 PHP SDK4.3 创建的腾讯云持久证书&#xff08;如果…

【添加公众号】CSDN官方指定推广功能

一、场景 二、说明 三、要求&#xff08;其中之一&#xff09; 三、实战 Stage 1&#xff1a;进入推广管理 Stage 2&#xff1a;申请推广 1、微信公众号推广 2、微信号推广 Stage 3&#xff1a;提交审核 Stage 4&#xff1a;查看结果 Stage 5&#xff1a;开启推广 S…

2024年Java基础面试题

2024年Java实战面试题&#xff08;北京&#xff09;_java 5 年 面试-CSDN博客 一、redis基础类型&#xff1a;string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、list&#xff08;列表&#xff09;、set&#xff08;集合&#xff09;、sort set &#…

深度学习与神经网络pytorch版 2.3 线性代数

深度学习与神经网络pytorch版 2.3 线性代数 目录 深度学习与神经网络pytorch版 2.3 线性代数 1. 简介 2. 线性代数 2.3.1 标量 ​编辑2.3.2 向量 2.3.3 矩阵 2.3.4 张量及其性质 2.3.5 降维 2.3.6 非降维求和 2.3.7 点积 2.3.8 矩阵-向量积 2.3.9 矩阵-矩阵乘法 …