C++雾中风景17:模板的非推断语境与std::type_identity
乍一看这个标题很玄乎,但是其实这只是涉及一个很简单的CPP的模板推导的知识点。
笔者近期进行CPP开发工作时,在编译时遇到了如下的模板类型的推断错误:note: candidate template ignored: deduced conflicting types for parameter T (long long vs. long int)。通过一番梳理之后总结成文,希望对大家有所帮助。
1.非推断语境
众所周知,函数模板的使用是C++编译期进行类型推导的过程。通过分析源代码之中函数实参的类型,进一步推断出调用的函数参数的类型,从而自动生成对应的函数,来达到精简代码逻辑的效果。
而所谓非推断语境呢?则是模板的类型不参与模板实参推导,取而代之地使用可在别处推导或显式指定的模板实参。
单看上述文字可能很难理解,咱们直接看代码就能明白了。
2.举个栗子
我们先来看看下面的一段简单的代码:
template<typename T>
struct TestTemplate {
T t;
};
template<typename T>
T add(TestTemplate<T>& test, T val) {
return test.t + val;
}
int main() {
TestTemplate<long> test_template{100};
return add(test_template, 10);
}
在进行编译的时候出现如下的报错:
note: template argument deduction/substitution failed:
note: deduced conflicting types for parameter 'T' ('long int' and 'int')
通过gcc的编译报错我们可以看出,这里出现了错误的模板推断问题。模板函数add在进行类型推断时出现了冲突,在同一个函数中,模板类型T被同时推断为long与int。
我们来分析一下模板推断的流程。
- 首先,参数
test_template的类型为TestTempalate<long>, 它作为add函数的第一个参数传入,此时T的类型被推导为了long。 - 接着,参数
val的类型为int, 它作为add函数的第二个参数传入,而此时由于13为int类型,所以T被推导为int类型。
正是因为这样,在add函数进行模板推导的过程之中,两个参数test与val同时参与了模板类型的推导,导致出现了上述的问题。
我们可以尝试将add函数的调用改为如下:add(test_template, 10l)。此时val也作为参数T也被推导为long类型,则编译不再报错。
3. 利用非推断语境解决问题
显然,上面的代码我们希望编译器支持将int类型自动推导为long,而不要出现恼人的报错。那我们就需要利用非推断语境来解决问题了,让val的类型不要参与到类型推导过程之中来,那么问题就解决了。
模板的非推断语境出现比较复杂,有需要的可以参考cppreference部分的详细解释。我们将利用第一种,也是最常见的非推断语境来解决上文提到的问题。
The nested-name-specifier (everything to the left of the scope resolution operator ::)
简单来说就是::左侧作用域的类型,不参与模板类型的推导。
所以上述代码改为如下代码,就可以规避原先的问题了。
template<typename T>
struct TestTemplate {
T t;
};
template<typename T> struct identity { typedef T type; };
template<typename T>
T add(TestTemplate<T>& test, typename identity<T>::type val) {
return test.t + val;
}
int main() {
TestTemplate<long> test_template{1000};
return add(test_template, 10);
}
这里我们新添加了类型identity, 并利用typename identity<T>::type规避了模板的类型推断过程,从而让val的类型推断直接利用了test参数的类型推断结果,所以此时val的类型为long,模板类型推断也就不再出错了。
正是因为非推断语境在模板推断中会被使用,所以C++20提供了新的trait:
std::type_identity与std::type_identity_t来帮助我们解决上述的问题。它们的实现与功能与上面展示的identity一致,都是利用模板的非推断语境来规避类型推断不同导致的编译失败问题。
4.小结
C++的一些模板推断的问题常常让人抓狂,很多时候gcc给出的一长串报错很容易劝退萌新。本篇聊了聊笔者实际在开发中遇到的模板推断问题出发,一步步分析报错,希望大家对解决编译问题有耐心,并擅用搜索引擎,功力必不唐捐。(当然,更新的C++标准也给我们解决问题的武器库添砖加瓦,多多学习才是正道,日常一念:C++20好~~~)
希望大家能够有所收获,笔者水平有限。成文之处难免有理解谬误之处,欢迎大家多多讨论,指教。
5.参考资料
C++雾中风景17:模板的非推断语境与std::type_identity的更多相关文章
- C++语言基础(20)-模板的非类型参数
一.在函数模板中使用非类型参数 #include <iostream> using namespace std; template<class T> void Swap(T & ...
- scala学习手记17 - 容器和类型推断
关于scala的类型推断前面已经提到过多次.再来看一下下面这个例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var ...
- 【模板】 非旋转treap
模板:luogu P3369 [模板]普通平衡树 code: #include <cstdio> #include <cstdlib> const int MAX_N=1000 ...
- java基础17 模板模式
1.模版模式 解决某类事物的步骤有些是固定的,有些会发生改变的,那么这个时候我们可以为这一类事物提供一个模版代码,从而提高效率. 2.模版模式的步骤 1.先写出解决该类事物的其中一种解决方案; ...
- 【转】 史上最详尽的平衡树(splay)讲解与模板(非指针版spaly)
ORZ原创Clove学姐: 变量声明:f[i]表示i的父结点,ch[i][0]表示i的左儿子,ch[i][1]表示i的右儿子,key[i]表示i的关键字(即结点i代表的那个数字),cnt[i]表示i结 ...
- 【模板】非旋转Treap
Treap,也叫做树堆,是指有一个随机附加域满足堆的性质的二叉搜索树. 如果一棵二叉搜索树插入节点的顺序是随机的,那我们得到的二叉搜索树在大多数情况下是平衡的,期望高度是log(n). 但有些情况下我 ...
- c++11-17 模板核心知识(三)—— 非类型模板参数 Nontype Template Parameters
类模板的非类型模板参数 函数模板的非类型模板参数 限制 使用auto推断非类型模板参数 模板参数不一定非得是类型,它们还可以是普通的数值.我们仍然使用前面文章的Stack的例子. 类模板的非类型模板参 ...
- 现代C++之理解模板类型推断(template type deduction)
理解模板类型推断(template type deduction) 我们往往不能理解一个复杂的系统是如何运作的,但是却知道这个系统能够做什么.C++的模板类型推断便是如此,把参数传递到模板函数往往能让 ...
- C++17尝鲜:类模板中的模板参数自动推导
模板参数自动推导 在C++17之前,类模板构造器的模板参数是不能像函数模板的模板参数那样被自动推导的,比如我们无法写 std::pair a{1, "a"s}; // C++17 ...
随机推荐
- COM技术中的VARIANT and VARIANTARG
VARIANT and VARIANTARG Use VARIANTARG to describe arguments passed within DISPPARAMS, and VARIANT to ...
- 开发在线教育平台项目步骤(Python3.7.6 + Django 3.0)
1. 新建虚拟环境 mkvirtualenv eduonline 2. 安装mysql数据库 pip install mysqlclient pip install pymysql 3. 在setti ...
- 使用pycallgraph分析python代码函数调用流程以及框架
技术背景 在上一篇博客中,我们介绍了使用量子计算模拟器ProjectQ去生成一个随机数,也介绍了随机数的应用场景等.但是有些时候我们希望可以打开这里面实现的原理,去看看在产生随机数的过程中经历了哪些运 ...
- 基于Hi3559AV100 RFCN实现细节解析-(3)系统输入VI分析一 :
下面随笔系列将对Hi3559AV100 RFCN实现细节进行解析,整个过程涉及到VI.VDEC.VPSS.VGS.VO.NNIE,其中涉及的内容,大家可以参考之前我写的博客: Hi3559AV100的 ...
- Java开发工程师最新面试题库系列——Mybatis框架部分(附答案)
Mybatis Mybatis是什么框架? 答:持久层框架 Mybatis和ORM有什么区别? 答:ORM是对象关系映射的一种设计理念,也就是对象属性对应数据库字段,让开发人员以操作对象的方式操作数据 ...
- ZooKeeper 基本概念并介绍RPC中Netty和Zookeeper的使用
前言 ZooKeeper 是一个分布式协调服务,可用于服务发现,分布式锁,分布式领导选举,配置管理等.Zookeeper提供一个类似Linux文件系统的属性结构,每个节点可存储少量的内存文件,并提供每 ...
- VSCode添加用户代码片段,自定义用户代码片段
在使用VScode开发中经常会有一些重复使用的代码块,复制粘贴也很麻烦,这时可以在VScode中添加用户代码片段,输入简写即可快捷输入. VScode中添加用户自定义代码片段很简单. 1.在VScod ...
- HashMap源码阅读(小白的java进阶)
OverView 构造方法 //构造方法 public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < ...
- ASP.NET Core与Redis搭建一个简易分布式缓存
本文主要介绍了缓存的概念,以及如何在服务器内存中存储内容.今天的目标是利用IDistributedCache来做一些分布式缓存,这样我们就可以横向扩展我们的web应用程序. 在本教程中,我将使用Re ...
- 攻防世界 reverse parallel-comparator-200
parallel-comparator-200 school-ctf-winter-2015 https://github.com/ctfs/write-ups-2015/tree/master/sc ...