C++14使用std::integer_sequence展开tuple作为函数的参数

元组是一种长度固定的允许有不同类型元素的集合,根据元素的个数不同又分别称作一元组、二元组、三元组等。C++11中标准库增加了一个叫std::tuple的类模板,用于表示元组。

下面的代码演示了使用C++创建一个三元组。

auto tuple = std::make_tuple(1, ‘A‘, "test");

std::cout << std::get<0>(tuple) << std::endl;

std::cout << std::get<1>(tuple) << std::endl;

std::cout << std::get<2>(tuple) << std::endl;

// 下面是C++14的特性

std::cout << std::get<int>(tuple) << std::endl;

std::cout << std::get<char>(tuple) << std::endl;

std::cout << std::get<const char*>(tuple) << std::endl;

输出

1

A

test

1

A

test

许多编程语言如C#、Python等也有tuple的概念。下面的代码演示了使用Python创建一个三元组。

t = (1, ‘A‘, "test")

print(t[0])

print(t[1])

print(t[2])

输出

1

A

test

Python从语言级别上支持将tuple展开为函数的参数,在Python中假设定义有这样一个函数func和一个元组t,下面的代码演示了将元组t的每个元素作为func函数的参数。

def func(arg1, arg2, arg3):

print(arg1, arg2, arg3)

t = (1, ‘A‘, "test")

func(*t)

输出

1

1 A test

可变参数模板(Variadic Template)

C++没有并没有对类似Python的这种特性提供语言或库的支持,本文的目的就是为了介绍如何在C++中实现将tuple展开作为函数的参数。假设有函数func和元组tuple如下:

void func(int arg1, char arg2, const std::string& arg3);

{

// ...

}

auto tuple = std::make_tuple(1, ‘A‘, "test");

手动将tuple中的元素逐个取出后作为参数调用应是下面这样的。

1

func(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple));

观察手动调的参数,可以看出对于N元组,调用函数时的参数是这样的一个列表。

std::get<0>(t), std::get<1>(t), ……, std::get<N – 1>(t)

于是可以使用C++11的可变参数模板(Variadic Template)写一个这样的函数模板apply

template<std::size_t... I, typename F, typename T>

void apply(F f, const T& t)

{

func(std::get<I>(t)...);

}

其中第1行中的std::size_t… I称为模板参数组(Template Parameter Pack),第4行的std::get<I>(t)..称为参数组展开(Parameter Pack Expansion)。

使用这个函数模板apply将tuple展开作为参数调用func函数是这样写的。

1

apply<0, 1, 2>(func, tuple);

显然这样的调用方式还不够优雅,因为需要手动写模板参数。对于N元组,这里的模板参数是这样一个序列。

0, 1, 2, 3, 4, …, N-1

如果能够使用模板参数推导(Template argument deduction)自动推导出这个序列就方便多了。C++14中的std::integer_sequence提供了这种机制。

std::integer_sequence

C++14中标准库增加了std::integer_sequence类模板用于表示编译期的整数序列。其声明如下

template<class T, T... Ints>

class integer_sequence;

下面是各模板参数的描述

T 整数序列元素的类型

…Ints 整数序列的参数组(非类型)

为了方便使用,C++14的标准库中还使用C++11的模板别名(Template Typedef或Template Alias)特性声明了下面这几个辅助使用的别名模板。

template<std::size_t... Ints>

using index_sequence = std::integer_sequence<std::size_t, Ints...>;

template<class T, T N>

using make_integer_sequence = std::integer_sequence<T, /* a sequence 0, 1, 2, ..., N-1 */>;

template<std::size_t N>

using make_index_sequence = make_integer_sequence<std::size_t, N>;

template<class... T>

using index_sequence_for = std::make_index_sequence<sizeof...(T)>;

下面的代码演示了使用std::integer_sequence创建一个含有元素0, 1, 2, 3, …, 9的vector。通过第13行的模板参数10,推导出第5行的模板参数组为0, 1, 2, 3, …, 9。

#include <utility>

#include <vector>

#include <iostream>

template<std::size_t... I>

std::vector<std::size_t> make_index_vector(std::index_sequence<I...>)

{

return {I...};

}

int main()

{

auto vec = make_index_vector(std::make_index_sequence<10>());

for(auto i : vec) {

std::cout << i << ‘ ‘;

}

std::cout << std::endl;

}

输出

1

0 1 2 3 4 5 6 7 8 9

使用std::integer_sequence实现apply函数模板

对于tuple可以使用std::tuple_size获取元组的元素个数,类似于前面创建vector使用std::integer_sequance实现apply函数模板的代码如下。

template<typename F, typename T, std::size_t... I>

void apply_impl(F f, const T& t, std::index_sequence<I...>)

{

f(std::get<I>(t)...);

}

template<typename F, typename T>

void apply(F f, const T& t)

{

apply_impl(f, t, std::make_index_sequence<std::tuple_size<T>::value>());

}

至此,使用apply函数模板时就不再需要手动写模板参数了。

apply(func, tuple);

缺点是如果func函数有返回值,其返回值会被忽略。对于返回值的问题,可以使用C++11的新的函数声明语法(New Function Declarator Syntax)特性来解决。

template<typename F, typename T, std::size_t... I>

auto apply_impl(F f, const T& t, std::index_sequence<I...>) -> decltype(f(std::get<I>(t)...))

{

return f(std::get<I>(t)...);

}

template<typename F, typename T>

auto apply(F f, const T& t) -> decltype(apply_impl(f, t, std::make_index_sequence<std::tuple_size<T>::value>()))

{

return apply_impl(f, t, std::make_index_sequence<std::tuple_size<T>::value>());

}

最后附上apply函数模板的完整实现和使用演示的代码。

#include <tuple>

#include <iostream>

#include <string>

#include <utility>

int func1(int arg1, char arg2, double arg3, const std::string& arg4)

{

std::cout << "call func1(" << arg1 << ", " << arg2 << ", " << arg3 << ", " << arg4 << ")" << std::endl;

return 0;

}

int func2(int arg1, int arg2)

{

std::cout << "call func2(" << arg1 << ", " << arg2 << ")" << std::endl;

return arg1 + arg2;

}

template<typename F, typename T, std::size_t... I>

auto apply_impl(F f, const T& t, std::index_sequence<I...>) -> decltype(f(std::get<I>(t)...))

{

return f(std::get<I>(t)...);

}

template<typename F, typename T>

auto apply(F f, const T& t) -> decltype(apply_impl(f, t, std::make_index_sequence<std::tuple_size<T>::value>()))

{

return apply_impl(f, t, std::make_index_sequence<std::tuple_size<T>::value>());

}

int main()

{

using namespace std::literals::string_literals;

auto tuple1 = std::make_tuple(1, ‘A‘, 1.2, "test"s);

auto result1 = apply(func1, tuple1);

std::cout << "result1 = " << result1 << std::endl;

auto tuple2 = std::make_tuple(1, 2);

auto result2 = apply(func2, tuple2);

std::cout << "result2 = " << result2 << std::endl;

}

输出

call func1(1, A, 1.2, test)

result1 = 0

call func2(1, 2)

result2 = 3

时间: 2024-12-19 13:27:15

C++14使用std::integer_sequence展开tuple作为函数的参数的相关文章

C# 使用Tuple传递多个参数

Tuple是基于.NET Framework 4.0 及以上版本才有的.微软称它为元组,如果有三个参数那就是三元组.如 Tuple(T1, T2, T3) Tuple的命名空间在 System 很短吧,也就是说只要是基于.NET Framework 4.0 及以上版本的,创建项目就可以直接在Visual Studio中敲出来. 程序集: mscorlib(在 mscorlib.dll 中) 为什么要使用Tuple 来!先看看下面这个方法. public bool MyMethod(out str

14.8.2. 标准库定义的函数对象

#include<iostream> #include<string> #include<vector> using namespace std; /* 这个类很简单,它定义了一个操作:函数调用操作符,该操作符有一个形参 并返回形参的绝对值. 函数调用操作符必须声明为成员函数.一个类可以定义函数调 用操作符的多个版本,由形参的数目或类型加以区别 */ struct absInt { int operator()(int val) { return val<0 ?

Effective C++ .14 智能指针的拷贝与deleter函数

#include <iostream> #include <cstdlib> #include <memory> using namespace std; class Kiwi { private: int weight; public: Kiwi(int w) : weight(w) {} ~Kiwi() { cout<<"~Kiwi"<<endl; } int getWeight() {return weight;} };

【c++函数重载 参数分别为为int和float,但是传入3.14报错】

#include <iostream.h> class Base { public: void f(int x){ cout << "Base::f(int) " << x << endl; } void f(float x){ cout << "Base::f(float) " << x << endl; } }; void main(void) { Base *pb = new

14、函数输出参数、递归

1.函数 输入参数对于函数而言,相当于已经赋值了的变量,直接可用. 输出参数相当于定义了一个变量,需要在函数中为其赋值,然后调用函数的时候将所赋值带出函数. 例1: 输入a,b,c的值,判断是不是一个一元二次方程: class Program { public string fangcheng(double a, double b, double c, out double x1, out double x2) { double d = b * b - 4 * a * c; if (a == 0

Python之路【第三篇】:Python基础(14)——函数默认参数

# 默认参数:必须放置在形式参数列表的最后 # def send(name,xx = "ok"):# ...# # 使用默认参数# send("eric") #对形式参数的第一个元素赋值,第二个元素使用默认参数.# # 指定参数# send("eric","no") #对形式参数的第一个元素赋值,第二个元素的默认参数重新赋值.# # def send(mail_addr,xx = "ok",content,

es6函数的参数展开

参数中三个点的用法 效果图 三个点(收集剩余的参数)后面不能再接其他参数,否则报错 原文地址:https://www.cnblogs.com/malong1992/p/12128420.html

第21课 可变参数模板(2)_展开参数包

1. 可变参数模板函数 (1)递归函数方式展开参数包 ①一般需要提供前向声明.一个参数包的展开函数和一个递归终止函数. ②前向声明有时可省略,递归终止函数可以是0个或n个参数 (2)逗号表达式和初始化列表方式展开参数包 ①逗号表达式按顺序执行,返回最后一个表达式的值. ②initilizer_list可接受任意多个不同类型的参数. ③借助逗号表达式来展开包,并将返回的结果用于初始化initilizer_list. [编程实验]展开可变参数模板函数的参数包 #include <iostream>

Numpy&amp;Pandas

Numpy & Pandas 简介 此篇笔记参考来源为<莫烦Python> 运算速度快:numpy 和 pandas 都是采用 C 语言编写, pandas 又是基于 numpy, 是 numpy 的升级版本. 消耗资源少:采用的是矩阵运算,会比 python 自带的字典或者列表快好多 Numpy 学习 2.1 numpy属性 ndim:维度 shape:行数和列数 size:元素个数 举例说明: import numpy as np array = np.array([[1,2,3]