TimothyQiu's Blog

keep it simple stupid

C++11 Variadic Template

分类:技术

听说这个特性是很久以前了,总是读作「维拉迪克·坦普雷特」,一直没反应过来中文到底该叫什么,因为 C 时代的 Variadic Macro 我一直是很象形地读作「点点点」的 = =||

OK,扯远了。Variadic Template 对应中文应该是「可变参数模板」。

Parameter Pack

既然是可变参数,就需要通过某种方式来表示这些参数,而这里的解决方案就是 Parameter Pack 参数包,不知道可不可以简称「餐包」 =_,=

声明参数包的方法是在类型和名称之间加 ...

template<typename... Types> struct Tuple {};
Tuple<>           t0;   // Types 中不含参数
Tuple<int>        t1;   // Types 中包含一个参数:int
Tuple<int, float> t2;   // Types 中包含两个参数:int 和 float

template<typename... Types> void f(Types... args);
f();        // args 中不包含参数
f(1);       // args 中包含一个参数:int(1)
f(2, 1.0);  // args 中包含两个参数:int(2) 和 double(1.0)

上面的两个示例中,Types 称作模板参数包,args 称作函数参数包。

参数包所包含的参数的个数可以用 sizeof... 取得。

Pack Expansion

既然提出了参数包,把所有可变参数容纳其中,那么就需要存在将其解包的操作。与 C 中 va_list 一个参数一个参数地手动解包不同,参数包的 Pack Expansion 是一口气将所有的参数以某种形式展开:

template<int... Entries>
struct IntArray {
    int array[sizeof...(Entries)] = { Entries... };
};

template<typename... Types> void bar(Types... args) {}
template<typename... Types> void foo(Types... args) {
    bar(&args...);
}

所谓「以某种形式」展开,就是将 pattern ... 转换为逗号分隔的 pattern_1, pattern_2, ... , pattern_N 的形式。从上面的函数 foo 就可以看出,传给 bar 的是各个参数的地址(没啥大意义);即 void foo(a, b, c)&args... 会展开成 &a, &b, &c

std::tuple

作为一个可变参数模板的实际用例,C++11 还引入了 std::tuple 作为 std::pair 的推广形式(tuple 的意思即为元祖……哦不对,是元组……我是吃货我自重……),表示任意多个元素的组合。

使用 std::make_tupleauto 可以很方便地声明一个元组:

auto x = std::make_tuple(3, 0.14, std::string("pie")); // std::tuple<int, double, std::string>

而对于各个元素的访问可以统一使用 std::get 实现(包括 std::arraystd::pair 的大一统):

auto element = std::get<2>(x);

另一个好玩的地方是使用 std::tie 创建 lvalue reference 的 tuple:

std::set<int> some_instance_of_std_set;
std::set<int>::iterator itr;
bool success;
std::tie(itr, success) = some_instance_of_std_set.insert(2012);

虽然看着有些丑陋,但似乎可以看到些「多返回值」的影子……

当然,也可以参照 Lua 中的 _ 使用 std::ignore 忽略多返回值中的特定位置的值:

int r1, r2;
std::tie(r1, std::ignore, r2) = std::make_tuple(3, 0.14, 4);

顺带的,既然是 lvalue reference,试图一句话交换两个变量的值是不可以全用 std::tie 的:

std::tie(a, b) = std::tie(b, a);        // 错误方式
std::tie(a, b) = std::make_tuple(b, a); // 正确方式

C++C++11

添加新评论 »