C++11新特性:变长模版

C++11新特性:变长模版

该内容总结自深入理解C++11:C++11新特性解析与应用

变长函数

变长函数是c语言的一个特性,使得函数能接受任何长度的参数列表(例如printf函数)

#include <stdio.h>
#include <stdarg.h>

double SumOfFloat(int count, ...){
	va_list ap;
	double sum = 0;
	va_start(ap, count);
	for(int i = 0; i < count; i++)
		sum += va_arg(ap, double);
	va_end(ap);
	return sum;
}

这段代码是一个简单的变长函数的例子,函数须传入参数数量count,在函数体内通过va_start对va_list类型的变量ap做初始化,后续代码使用va_arg函数将参数一一取出。

变长模版:模版参数包和函数参数包

模版参数包

语法
// 类型
template <typename... Elements> class ClassName{};
ClassName<int, char, double> cn;
// 非类型
template<int... A> class NonTypeTemplate{};
NonTypeTemplate<1, 0, 2> ntvt;

在c++11中,Elements、A就是所谓的模版参数包。

应用

为了使用模板参数包,在类中需要将其解包(unpack),通过一个名为包扩展(pack expansion的表达式来实现。

//定义
template<typename T1, typename T2> class B{};
template<typename... A> class Template: private B<A...>{};//包扩展A...

//使用: X, Y -> A -> A... -> X, Y
Template<X, Y> xy;

这个例子是基于Template内中对A…的使用总是接受两个参数的前提下,如果使用Template<X, Y, Z>就会发生模版推导的错误。
那么就可以通过递归进一步实现接受任意多的模版参数,通过简化tuple实现来举例。

//递归实现接受任意多的模版参数
//简化tuple实现举例
template <typename... Elements> class tuple; //变长模版的声明

template<typename Head, typename... Tail> 	//递归的偏特化定义
class tuple<Head, Tail...>: private tuple<Tail...>{
	Head head;
};

template<> class tuple<>{};				//边界条件,结束递归

tuple<double, int, char, float> tp;

这段代码可以简单理解,我调用<double, int, char, float>,在第一次时double作为head,<int, char, float>作为模版参数包继续递归传递,第二次int作为head,<char, float>继续传递,以此类推,最后实现一个包含<double, int, char, float>四个类型的tuple tp,通过下图可以更好理解这么一个原理。
在这里插入图片描述

函数参数包

语法
template<typename... T> void f(T... args);	//函数参数包:args

在C++11中,函数参数包有两个限制

  • 唯一
  • 函数的最后一个参数

ps:函数参数包和模版参数包其他差不多,故不做赘述

应用
#include <iostream>

// 基础函数,用于终止递归
void print() {
    std::cout << std::endl;
}

// 可变参数模板函数
template<typename T, typename... Args>
void print(T first, Args... args) {
    std::cout << first << " ";
    print(args...);  // 递归调用
}

int main() {
    print(1, 2.5, "Hello", 'a');  // 输出: 1 2.5 Hello a 
    print("C++", 11, 14, 17);     // 输出: C++ 11 14 17
    return 0;
}

变长模版:进阶

  1. 参数包展开位置

上面例子中,参数包在基类描述列表和函数体内做了展开,c++规定了7种参数包可以展开的位置

  • 表达式
  • 初始化列表
  • 基类描述列表
  • 类成员初始化列表
  • 模版参数列表
  • 通用属性列表

ps:这些都是什么不在这里做说明,可以自行了解

  1. B<A>…与B<A…>的区别

// B<A>...
template<typename... Ts>
struct TupleOfB {
    std::tuple<B<Ts>...> data; // 展开为 B<T1>, B<T2>, ..., B<Tn>
};

TupleOfB<int, float, char> t; // 等价于 std::tuple<B<int>, B<float>, B<char>>

//B<A...>
template<typename... Args>
struct B {};

template<typename... As>
struct Foo {
    B<As...> b; // 把 As... 的所有参数一次性传给 B
};

Foo<int, float, char> f;	// 等价于 B<int, float, char>

  1. sizeof…

int size = sizeof...(A);  //计算变长包的长度
  1. 完美转发参数包

template<typename T, typename... Args>
T create(Args&&... args) {
    // 完美转发参数给构造函数
    return T(std::forward<Args>(args)...);
}

class MyClass {
public:
    MyClass(int a, double b, const std::string& c) {
        std::cout << "Constructed with: " << a << ", " << b << ", " << c << "\n";
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值