元组是一种长度固定的允许有不同类型元素的集合,根据元素的个数不同又分别称作一元组、二元组、三元组等。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

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

  1. Python3学习(一)-基础、数据类型、变量、字符串和编码、list&tuple、if、for、while、dict、set、函数与参数

    ##廖雪峰py3笔记 ## '//'这是获得相除后的整数部分 ##a = 10//3 ##print (a) ## '/'获得相除后的结果,为浮点数,结果能整除也也是浮点数 ##b = 10/3 ## ...

  2. C# 使用Tuple传递多个参数

    Tuple是基于.NET Framework 4.0 及以上版本才有的.微软称它为元组,如果有三个参数那就是三元组.如 Tuple(T1, T2, T3) Tuple的命名空间在 System 很短吧 ...

  3. 关于dictionary和tuple充当函数参数

    需要接收dict时,使用 **name: 需要接收tuple时,使用 *name: --> *name参数后面的任何数据会被认为是’keyword-only’,即它们只能被当作关键词而非参数使用 ...

  4. 使用std::function 把类成员函数指针转换为普通函数指针

    前言 这是改造前一篇 设计模式 的基础,使通知者不必知道观察者的类名和函数名,只需要知道更新函数的原型即可. 开发环境:WIN7 32位 + VS2010 发现在VS2005中使用std::funti ...

  5. org.apache.commons.lang3.tuple.Pair 作为更新参数,XML 中的 Sql 取不到值、报错

    项目用的 Mybatis,今天改一个需求,落地实现是批量更新,且只需要根据主键(id)来更新一个字段(name). 于是,没有犹豫,像下面这样设计了数据结构: 既然是批量更新,那外层肯定是 List ...

  6. 一个std::sort 自定义比较排序函数 crash的分析过程

    两年未写总结博客,今天先来练练手,总结最近遇到的一个crash case. 注意:以下的分析都基于GCC4.4.6 一.解决crash 我们有一个复杂的排序,涉及到很多个因子,使用自定义排序函数的st ...

  7. python tuple的函数

    1. len(tuple) 计算元组元素个数 >>> tuple1 = ('Google', 'Runoob', 'Taobao') >>> len(tuple1) ...

  8. 2013级C++第14周(春)项目——多态性、虚函数和抽象类

    课程首页在:http://blog.csdn.net/sxhelijian/article/details/11890759,内有完整教学方案及资源链接 第一部分 阅读程序1.阅读.改动和执行关于交通 ...

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

    #include <iostream> #include <cstdlib> #include <memory> using namespace std; clas ...

随机推荐

  1. C++中this指针的用法详解(转)

    原文地址:http://blog.chinaunix.net/uid-21411227-id-1826942.html 1. this指针的用处: 一个对象的this指针并不是对象本身的一部分,不会影 ...

  2. ServiceStack

    https://github.com/ServiceStack/ServiceStack/wiki/Routing Service Gateway https://github.com/Service ...

  3. Android如何防止apk程序被反编译

    作为Android应用开发者,不得不面对一个尴尬的局面,就是自己辛辛苦苦开发的应用可以被别人很轻易的就反编译出来. Google似乎也发现了这个问题,从SDK2.3开始我们可以看到在android-s ...

  4. 011. asp.net内置对象

    Response对象: Response代表了服务器响应对象, 主要用于将数据从服务器发送回浏览器; 每次客户端发出一个请求的时候,服务器就会用一个响应对象来处理这个请求,处理完这个请求之后,服务器就 ...

  5. unity, 同步物体坐标一定要在LateUpdate中进行

    设a为主动物体,b为跟随物体. 如果a,b都在同一个Update里更新坐标,那么两者自然是同步的. 如果a在a.Update里更新位置,而b在b.Update里将自己的位置更新为与a相同,那就会有误差 ...

  6. java从零到变身爬虫大神(一)

    学习java3天有余,知道一些基本语法后 学习java爬虫,1天后开始出现明显效果 刚开始先从最简单的爬虫逻辑入手 爬虫最简单的解析面真的是这样 import org.jsoup.Jsoup; imp ...

  7. Oracle获取AWR和ASH

    -- 找到指定的snap select snap_id, max(sample_time) from sys.wrh$_active_session_history group by snap_id ...

  8. shell随机写入指定文件

    具体思路如下,求代码 shell随机读取file.txt文本,file.txt文本有许多行文字 想用shell随机读取file.txt的任意一行文字并且写入到file.php第四行文件. sed -i ...

  9. java生成随机字符串uuid

    GUID是一个128位长的数字,一般用16进制表示.算法的核心思想是结合机器的网卡.当地时间.一个随即数来生成GUID.从理论上讲,如果一台机器每秒产生10000000个GUID,则可以保证(概率意义 ...

  10. TP控制器(Controller)

    控制器的一些方法: Maincontroller.class.php文件: <?php namespace Home\Controller; use Think\Controller; clas ...