模板参数推导

模板参数推导(template argument deduction),是在调用C++的模板函数时,由编译器根据使用上下文来推断所调用的模板函数的模板参数。这一概念也适用于类的模板成员函数。

类模板也存在模板参数推导的情形。例如:

template <class T>

struct eval;

template <template <class, class...> class TT, class T1, class... Rest>

struct eval<TT<T1, Rest...>>

{ };

eval<A<int>> eA; // OK: matches partial specialization of eval

概念

模板函数在定义时,用template<>声明模板参数(或称模板形参)。调用模板函数时,可以在函数名字后用< >显式指出模板参数(这时称作模板实参)。例如:

template<class T> void foo(T v1){}; foo<double>(3);

但是,调用模板函数时,如果不显式指明模板参数,而是根据函数的调用实参去推断模板实参,这就是模板参数推导。例如上例可进一步考虑情形:

foo(3.14);

编译器会推导出这种函数调用的模板实参为T->double.

推导类型

编译器比较函数模板的形参(template parameter)与对应的调用实参(argument used in the function call)的类型,以确定模板参数的类型。形参的类型必须是下述特定情形之一:

T const T volatile T T& T* T[10] A<T> C(*)(T) T(*)() T(*)(U) T C::* C T::* T U::* T (C::*)() C (T::*)() D (C::*)(T) C (T::*)(U) T (C::*)(U) T (U::*)() T (U::*)(V) E[10][i] B<i> TT<T> TT<i> TT<C>

说明:

  • T, U, V表示模板类型参数
  • 10表示任意整数常量
  • i表示模板非类型参数
  • [i]表示数组界( represents an array bound of a reference or pointer type, or a non-major array bound of a normal array)
  • TT表示模板的模板参数(template template argument)
  • (T), (U), (V)表示参数列表包含至少一个模板类型参数
  • ()表示参数列表不包含模板参数
  • <T>表示模板参数列表包含至少一个模板类型参数
  • <i>表示模板参数列表包含至少一个模板非类型参数
  • <C>表示模板参数列表其模板参数不依赖于模板实参

编译器可以从上述几个类型结构的复合类型推导模板参数。

推导规则

对于形如:

template<typename T> int foo(ParamType param);

其模板参数T的类型需要从模板函数的实参与形参依照如下规则推导:

  • 首先,如果模板函数的实参表达式是引用,首先去除引用;
  • 上一步后,如果剩下的实参表达式有顶层的const且/或volatile限定符,去除掉。

因而,从模板函数的实参表达式,不能自动推导出顶层的CV-qualifiers,也不能自动推导出引用类型,需要显式指定。

例如:

template<class T> void foo(T arg){ arg=101;} // 函数模板 const int i=102; foo(i); //函数模板实例化为 void foo<int>(int),实参的const限定已被脱去 foo<const int&>(v1);//直接显示指明模板参数类型 template<class T> void foo(const T& arg);//或者偏特化模板函数

如果形参还带上&号,声明为引用类型,则不执行const剥除(const-stripping),例如:

const int i=102; const int &j=i; template<class T> void foo(T arg) { arg=101;}// 函数模板 foo(j); // 编译错误: assignment of read-only reference ‘arg‘|

这是因为如果不抑制const剥除,则得到了一个非常量引用型变量,绑定到const变量,这显然是不可接受的。

实参表达式为数组,模板参数推导的类型为指针。这是因为数组名在实参表达式中自动隐式转换为首元素地址的右值。例如:

int a[9]; template<class T> void foo(T arg){}; // 函数模板 foo(a);// 函数模板实例化为 void foo<int*>(int*)

另外,C++11标准明确规定不能由模板参数推导出对应实参为std::initializer_list的类型。例如:

template<class T> void g(T); g({1,2,3}); // error: no argument deduced for T

类型完美转发

C++11增加了右值引用这一新的数据类型。如:template<class T> void foo(T&& arg);“T&&”并不意味着形参arg的数据类型一定是右值引用。其数据类型既可能是左值引用,也可能是右值引用。依据“引用塌缩规则”,有:

  • 如果实参表达式是类型A的左值,则模板参数T的类型为左值引用A&,形参arg的类型为左值引用A&;
  • 如果实参表达式是类型A的右值(包括纯右值临终值),则模板参数T的类型为右值引用A&&,形参arg的类型为y右值引用A&&。

如此,就把实参的值分类情形完美地传递到模板函数内部,据此可再完美转发给拷贝语义或移动语义的实现函数。

标签:

模板模板参数

... 、非类型参数、模板作为参数); 如果二者是非类型的模板参数,则其具体类型应相等; 如果二者是模板作为参数,则递归执行上述模板模板参数形实匹配规则; 如果P是一个可变参数模板,即P的模板形参列表包含了模板参数包(template parameter pack),则这个模板参数包 ...

C++14

... 了这一要求,允许lambda函数的形式参数声明中使用类型说明符 auto 。 auto lambda = [](auto x, auto y) {return x + y;} 泛型lambda函数遵循模板参数推导的规则。以上代码的作用与下面的代码相同: struct unnamed_lambda { template auto operator()(T x, U y) const {return x + y ... 的新特性,诸如移动和in-place构造 ...

C++ Technical Report 1

... C++语言中的引用。 使用 ref 以获得任何实例的wrapper reference(对常数引用const &使用 cref )。 wrapper reference对模板函数(template function)尤其有用,当模板参数推导不出引用的时候(范例如下:) void f( int &r ) { r++; } template< class Funct, class Arg > void g( Funct f, Arg t ... ...

Initializer list

... constructor 注意,仅可以从braced-init-list推导出initializer_list。这一过程中,编译器自动查询哪些构造函数可以用initializer_list作为参数,并据此确定initializer_list的模板参数T的类型,从而对braced-init-list做类型转换,例如把字符串书面量"Hello world!"转换为string类,然后生成initializer_list,最后 ... 例如: int i {3. ...

可变参数模板

... ++11之前,模板(类模板与函数模板)在声明时必须有 固定数量的模板参数。C++11允许模板定义有任意类型任意数量的模板参数。 template class tuple; 上述模板类tuple可以有任意个数的类型名(typename)作为它的模板形参(template parameter)。例如,上述模板类可以 ... 现在形参名字左侧,声明了一 ...

模板超编程

... 如下的程式码: template Vector& Vector::operator+=(const Vector& rhs) { value[0] += rhs.value[0]; value[1] += rhs.value[1]; return *this; } 因为模板参数 dimension 在编译期是常数,所以编译器应能展开 for 循环。 静态多型 多型是一项共通的标准编程工具,衍生类别 ... 用来达成静态多型。如下例: template struct ...

decltype

... 设计,以解决泛型编程中有些类型由模板参数决定而难以(甚至不可能)表示的问题。 从语义上说,decltype的设计适合于通用库编写者或编程新手。总体上说,对于变量或函数参数作为表达式,由decltype推导出的类型与源码中的定义 ... 的机制的需求便由此出现,而这一机制常称为typeof。在泛型编程中,若类型由函数参数决定,则获知之常非易事,在需要获取函数模板实例化的返回类型时尤然。 为此,许多编译器厂商都基于程序 ...

右值引用

... push_back(T&& x); // T是类模板参数 ? 该成员函数不需要类型推导;这里的函数参数类型就是T的右值引用 template void emplace_back(Args&& args); // 该成员函数是个函数模板,有自己的模板参数,需要类型推导 }; 函数模板的形参必须是T&&形式,才需要模板参数类型推导。 ...

MediaWiki

... 2004年8月11日 完整版本说明 新的高度使用CSS呈现的默认外观(“MonoBook”皮肤),并进一步地遵从网络标准。 模板参数功能。 分类功能。 在可能时自动合并编辑冲突。 改善安装程序。 1.4 2005年3月20日 完整版本说明、语言 ... 5日 完整版本说明、语言支持 使用新的扩展框架处理页面的保护和解除保护。 后台更新采用“工作队列”。 改进对模板使用的溯查。 模板参数可使用默认值。 1.7 2006年7月7日 ...

时间: 2024-07-30 10:17:10

模板参数推导的相关文章

error C2783: 无法为“T”推导 模板 参数

原则:“模板参数推导机制无法推导函数的返回值类型” 版本一: // 缺少<T> 参数 int n 对比第三个版本( 缺少<T> 参数 T n) ! 编译错误提示: 错误 1 error C2783: “T FibRecursion(int)”: 无法为“T”推导 模板 参数 c:\users\yi\documents\visual studio 2005\projects\斐波纳契数列\斐波纳契数列\斐波纳契数列.cpp 16 1 #include "stdafx.h&q

C++11 函数模板的默认模板参数

类模板:通用的类描述(使用泛型来定义类),进行实例化时,其中的泛型再用具体的类型替换. 函数模板:通用的函数描述(使用泛型来定义函数),进行实例化时,其中的泛型再用具体的类型替换. [1]C++98标准中两者的区别 函数模板和类模板在C++98标准中一起被引入,两者区别主要在于: 在类模板声明时,标准允许其有默认模板参数.而函数模板却不支持. 默认模板参数的作用如同函数的默认形参.不过在C++11中,这一限制已经被解除了,如下例所示: 1 void DefParm(int m = 3) {} /

C++11 图说VS2013下的引用叠加规则和模板参数类型推导规则

背景:    最近在学习C++STL,出于偶然,在C++Reference上看到了vector下的emplace_back函数,不想由此引发了一系列的“探索”,于是就有了现在这篇博文. 前言:      右值引用无疑是C++11新特性中一颗耀眼的明珠,在此基础上实现了移动语义和完美转发,三者构成了令很多C++开发者拍案叫绝的“铁三角”(当然不是所有C++开发者).而在这个“铁三角”中,有一个无法回避的关键细节,那就是引用叠加规则和模板参数类型推导规则.其实,关于这两个规则,可查到的资料不少,但都

C++ Primer 学习笔记_45_模板(三):缺省模板参数(借助标准模板容器deque实现Stack模板)、成员模板、关键字typename

一.缺省模板参数 1.stack内存能否借助标准模板容器管理呢?答案是肯定的,只需要多传一个模板参数即可,而且模板参数还可以是缺省的,如下: template <typename T, typename CONT = std::deque<T> > //此处末尾必须有空格,否则编译出错 class Stack { - private: CONT c_; }; 如果没有传第二个参数,默认为deque 双端队列,当然我们也可以传递std::vector<T> 2.示例:借助

《Effective Modern C++》翻译--条款1: 理解模板类型推导

北京2016年1月9日13:47:17 开始第一章的翻译. 第一章名为 类型推断 分为四个条款: 1理解模板类型推导 2理解auto自动类型推导 3理解decltype操作符 4如何对待推导的类型 第一章 类型推导 C++98有一套单一的类型推导的规则用来推导函数模板.C++11轻微的修改了这些规则并且增加了两个推导规则,一个用于auto,一个用于decltype.接着C++14扩展了auto和decltype可以使用的语境.类型推导的普遍应用将程序员从必须拼写那些显然多余的类型中解放了出来,它

零值初始化&amp;字符串常数作为函数模板参数

1.在定义一个局部变量时,并希望该局部变量的初始化一个值,可以显示调用其默认构造函数,使其值为0(bool类型默认值为false). template <typename T> void foo() { T x = T(); } 类模板其成员有可能被参数化.为了确保初始化这样的成员,必须定义一个构造函数,在成员初始化列表中对每个成员进行初始化: template <typename T> class MyClass { private: T x; public: MyClass:X

[014]模板-模板实参推导

对于函数模板,编译器利用调用中的函数实参来确定其函数模板,从函数实参来确定模板实参的过程就被叫做是模板实参推导. 比如: 1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 template <class T> 6 int compare(const T &v1, const T &v2) { 7 if (v1 > v2) { 8 cout << &quo

读书笔记 effective c++ Item 44 将与模板参数无关的代码抽离出来

1. 使用模板可能导致代码膨胀 使用模板是节省时间和避免代码重用的很好的方法.你不需要手动输入20个相同的类名,每个类有15个成员函数,相反,你只需要输入一个类模板,然后让编译器来为你实例化20个特定的类和300个你需要的函数.(只有在被使用的情况下类模版的成员函数才会被隐式的实例化,所以只有在300个函数被实际用到的情况下才会生成300个成员函数.)函数模板同样吸引人.你不用手动实现许多函数,你只需要实现一个函数模板,然后让编译器来做余下的事情. 然而在有些时候,如果你不小心,使用模板会导致代

c++ 模板参数做容器参数,迭代器报错 vector&lt;T&gt;::const_iterator

错误如下: template<class T>void temp(std::vector<T>& container){        std::vector<T>::const_iterator p; //error: expected ‘;’ before ‘p’        for(p = container.begin(); p != container.end(); ++p)        {                //...