乍一看这个标题很玄乎,但是其实这只是涉及一个很简单的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.参考资料

CppReference

C++雾中风景17:模板的非推断语境与std::type_identity的更多相关文章

  1. C++语言基础(20)-模板的非类型参数

    一.在函数模板中使用非类型参数 #include <iostream> using namespace std; template<class T> void Swap(T & ...

  2. scala学习手记17 - 容器和类型推断

    关于scala的类型推断前面已经提到过多次.再来看一下下面这个例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var ...

  3. 【模板】 非旋转treap

    模板:luogu P3369 [模板]普通平衡树 code: #include <cstdio> #include <cstdlib> const int MAX_N=1000 ...

  4. java基础17 模板模式

    1.模版模式 解决某类事物的步骤有些是固定的,有些会发生改变的,那么这个时候我们可以为这一类事物提供一个模版代码,从而提高效率. 2.模版模式的步骤 1.先写出解决该类事物的其中一种解决方案;     ...

  5. 【转】 史上最详尽的平衡树(splay)讲解与模板(非指针版spaly)

    ORZ原创Clove学姐: 变量声明:f[i]表示i的父结点,ch[i][0]表示i的左儿子,ch[i][1]表示i的右儿子,key[i]表示i的关键字(即结点i代表的那个数字),cnt[i]表示i结 ...

  6. 【模板】非旋转Treap

    Treap,也叫做树堆,是指有一个随机附加域满足堆的性质的二叉搜索树. 如果一棵二叉搜索树插入节点的顺序是随机的,那我们得到的二叉搜索树在大多数情况下是平衡的,期望高度是log(n). 但有些情况下我 ...

  7. c++11-17 模板核心知识(三)—— 非类型模板参数 Nontype Template Parameters

    类模板的非类型模板参数 函数模板的非类型模板参数 限制 使用auto推断非类型模板参数 模板参数不一定非得是类型,它们还可以是普通的数值.我们仍然使用前面文章的Stack的例子. 类模板的非类型模板参 ...

  8. 现代C++之理解模板类型推断(template type deduction)

    理解模板类型推断(template type deduction) 我们往往不能理解一个复杂的系统是如何运作的,但是却知道这个系统能够做什么.C++的模板类型推断便是如此,把参数传递到模板函数往往能让 ...

  9. C++17尝鲜:类模板中的模板参数自动推导

    模板参数自动推导 在C++17之前,类模板构造器的模板参数是不能像函数模板的模板参数那样被自动推导的,比如我们无法写 std::pair a{1, "a"s}; // C++17 ...

随机推荐

  1. ALGO基础(一)—— 排序

    ALGO基础(一)-- 排序 冒选插希快归堆,以下均为从小到大排 1 冒泡排序 描述: 比较相邻的元素.如果第一个比第二个大,就交换它们两个: 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一 ...

  2. Adapper 入门

    Adapper 入门 特点 单实体实现自动装配.连表查询需要自己处理装配,查看查询. 原生sql语句. 连接接口: IDbConnection connection = new SqlConnecti ...

  3. POJ-3026(图上的最小生成树+prim算法+gets函数使用)

    Borg Maze POJ-3026 一开始看到这题是没有思路的,看了题解才知道和最小生成树有关系. 题目的意思是每次走到一个A或者S就可以分为多个部分继续进行搜索.这里就可以看出是从该点分出去的不同 ...

  4. 如果一个网站存在CSRF漏洞,可以通过CSRF漏洞做下面那些事情?

    如果一个网站存在CSRF漏洞,可以通过CSRF漏洞做下面那些事情? 答:跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求.利用受害者在被攻击网站已经获取的注册凭证 ...

  5. Kubernetes 实战 —— 02. 开始使用 Kubernetes 和 Docker

    创建.运行及共享容器镜像 P23 运行容器 P24 运行 P24 可以运行 Docker 客户端可执行文件来执行各种 Docker 命令.例如:可以试着从 Docker Hub 的公共镜像仓库拉取.运 ...

  6. 【pytest官方文档】解读fixtures - 7. Teardown处理,yield和addfinalizer

    当我们运行测试函数时,我们希望确保测试函数在运行结束后,可以自己清理掉对环境的影响. 这样的话,它们就不会干扰任何其他的测试函数,更不会日积月累的留下越来越多的测试数据. 用过unittest的朋友相 ...

  7. 微信小程序在Android和Ios端的获取时间兼容性问题

    an端 var time = new Date() 例如:2020-01-01 01:01:00   ios端 var time = new Date() 例如:2020/01/01 01:01:00 ...

  8. Reverse 高校网络信息安全运维挑战赛

    Reverse 高校网络信息安全运维挑战赛 1 signed int sub_403CC0() 2 { 3 unsigned int v0; // eax 4 int key_lens; // eax ...

  9. python3表格数据处理

    技术背景 数据处理是一个当下非常热门的研究方向,通过对于大型实际场景中的数据进行建模,可以用于预测下一阶段可能出现的情况.比如我们有过去的2002年-2018年的黄金价格的数据: 该数据来源于Gite ...

  10. 最小生成树(Prim算法,Kruskal算法 )

    声明:图片及内容基于https://www.bilibili.com/video/BV1yp4y1Q74o?from=articleDetail 最小生成树原理 . 普利姆(Prim)算法 原理 Pr ...