C++11/14新特性--模板增强、variadic templates


一、模版增强

1、外部模版

1) 传统C++的问题

传统 C++ 中,模板只有在使用时才会被编译器实例化。
换句话说,只要在每个编译单元(文件)中编译的代码中遇到了被完整定义的模板,都会实例化。
这就产生了重复实例化而导致的编译时间的增加。并且,我们没有办法通知编译器不要触发模板实例化。


2)C++解决方法

C++11 引入了外部模板,扩充了原来的强制编译器在特定位置实例化模板的语法,使得能够显式的告诉编译器何时进行模板的实例化。关键字:extern

template class std::vector<bool>;            // 强行实例化
extern template class std::vector<double>;   // 不在该编译文件中实例化模板 


2、尖括号“>”

在传统 C++ 的编译器中,>> 一律被当做右移运算符来进行处理。
但实际上我们很容易就写出了嵌套模板的代码:

std::vector<  std::vector<int> > wow; 	//C++98写法,在> >中间增加空格
std::vector<std::vector<int>> wow; 		//C++98会被编译器认为是右移运算符

而从C++11 开始,第(2)种连续尖括号将变得合法,并且能够顺利通过编译。


3、类型别名模板

在了解类型别名模板之前,需要理解『模板』和『类型』之间的不同。仔细体会这句话:模板是用来产生类型的。
在传统 C++中,typedef 可以为类型定义一个新的名称,但是却没有办法为模板定义一个新的名称。因为,模板不是类型。

例如:

template< typename T, typename U, int value>
class SuckType
{
public:
    T a;
    U b;
    SuckType():a(value),b(value){}
};
template< typename U>
typedef SuckType<std::vector<int>, U, 1> NewType; // 不合法

C++11 使用using 引入了下面这种形式的写法,并且同时支持对传统typedef 相同的功效:

通常我们使用typedef定义别名的语法是:typedef 原名称 新名称;
但是对函数指针等别名的定义语法却不相同,这通常给直接阅读造成了一定程度的困难。

typedef int (*process)(void *);  		// 定义了一个返回类型为 int,参数为 void* 的函数指针类型,名字叫做 process
using process = int(*)(void *); 		// 同上, 更加直观

template <typename T>
using NewType = SuckType<int, T, 1>;    // 合法


4、默认模板参数

定义一个加法函数

template<typename T, typename U>
auto add(T x, U y) -> decltype(x+y)
{
    return x+y
}

但在使用时发现,要使用add函数,就必须每次都指定它的模板参数的类型。
在C++11中提供了一种便利,可以指定模板的默认参数:

template<typename T = int, typename U = int>
auto add(T x, U y) -> decltype(x+y)
{
    return x+y;
}


5、 变长参数模板

模板一直是C++所独有的黑魔法之一。在 C++11 之前,无论是类模板还是函数模板,都只能按其指定的样子,接受一组固定数量的模板参数;而 C++11 加入了新的表示方法,允许任意个数、任意类别的模板参数,同时也不需要在定义时将参数的个数固定。

template<typename… Ts> class Magic;

模板类 Magic 的对象,能够接受不受限制个数的 typename 作为模板的形式参数,例如下面的定义:

class Magic<int,  std::vector<int>,  std::map<std::string, std::vector<int>>> darkMagic;

既然是任意形式,所以个数为0的模板参数也是可以的:class Magic<> nothing;

如果不希望产生的模板参数个数为0,可以手动的定义至少一个模板参数:
template<typename Require, typename… Args> class Magic;

变长参数模板也能被直接调整到模板函数上。传统 C 中的 printf 函数,虽然也能达成不定个数的形参的调用,但其并非类别安全。而 C++11 除了能定义类别安全的变长参数函数外,还可以使类似 printf 的函数能自然地处理非自带类别的对象。除了在模板参数中能使用 … 表示不定长模板参数外,函数参数也使用同样的表示法代表不定长参数,这也就为我们简单编写变长参数函数提供了便捷的手段,例如:
template<typename… Args> void printf(const std::string &str, Args… args);


那么我们定义了变长的模板参数,如何对参数进行解包呢?
1)首先,我们可以使用sizeof…来计算参数的个数:

template<typename... Args>
void magic(Args... args)
{
    std::cout << sizeof...(args) << std::endl;
}
//我们可以传递任意个参数给 magic 函数:
magic();        // 输出0
magic(1);       // 输出1
magic(1, "");   // 输出2


2)其次,对参数进行解包,到目前位置还没有一种简单的方法能够处理参数包,但有两种经典处理手法。

手法1:递归模版函数
递归是非常容易想到的一种手段,也是最经典的处理方法。这种方法不断递归的向函数传递模板参数,进而达到递归遍历所有模板参数的目的。

#include <iostream>
template<typename T>
void printf(T value)
{
    std::cout << value << std::endl;
}
 
template<typename T, typename... Args>
void printf(T value, Args... args)
{
    std::cout << value << std::endl;
    printf(args...);
}
 
int main()
{
    printf(1, 2, "123", 1.1);
    return 0;
}


手法2:初始化列表展开
递归模板函数是一种标准的做法,但缺点显而易见的在于必须定义一个终止递归的函数。

这里介绍一种使用初始化列表展开的黑魔法:

template<typename T, typename... Args>
auto print(T value, Args... args)
{
    std::cout << value << std::endl;
    return std::initializer_list<T>
    {
        ([&] {std::cout << args << std::endl;}(), value)...
    };
}
 
int main()
{
    print(1, 2.1, "123");
    return 0;
}

通过初始化列表,(lambda 表达式, value)…将会被展开。由于逗号表达式的出现,首先会执行前面的 lambda 表达式,完成参数的输出。唯一不美观的地方在于如果不使用 return编译器会给出未使用的变量作为警告。


二、variadic templates

这里借助候捷老师的课件进行展示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
【为什么还需要学习C++?】 你是否接触很多语言,但从来没有了解过编程语言的本质?你是否想成为一名资深开发人员,想开发别人做不了的高性能程序?你是否经常想要窥探大型企业级开发工程的思路,但苦于没有基础只能望洋兴叹? 那么C++就是你个人能力提升,职业之路进阶的不二之选。【课程特色】 1.课程共19大章节,239课时内容,涵盖数据结构、函数、类、指针、标准库全部知识体系。2.带你从知识与思想的层面从0构建C++知识框架,分析大型项目实践思路,为你打下坚实的基础。3.李宁老师结合4大国外顶级C++著作的精华为大家推出的《征服C++11》课程。【学完后我将达到什么水平?】 1.对C++的各个知识能够熟练配置、开发、部署;2.吊打一切关于C++的笔试面试题;3.面向物联网的“嵌入式”和面向大型化的“分布式”开发,掌握职业钥匙,把握行业先机。【面向人群】 1.希望一站式快速入门的C++初学者; 2.希望快速学习 C++、掌握编程要义、修炼内功的开发者; 3.有志于挑战更高级的开发项目,成为资深开发的工程师。 【课程设计】 本课程包含3大模块基础篇本篇主要讲解c++的基础概念,包含数据类型、运算符等基本语法,数组、指针、字符串等基本词法,循环、函数、类等基本句法等。进阶篇本篇主要讲解编程中常用的一些技能,包含类的高级技术、类的继承、编译链接和命名空间等。提升篇:本篇可以帮助学员更加高效的进行c++开发,其中包含类型转换、文件操作、异常处理、代码重用等内容。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值