C++17之std::apply与std::make_from_tuple

C++17中有两个有意思的语法,是关于std::tuple(或std::pairstd::array等可以通过std::get来操作成员的元组容器)与函数参数转化的问题的。今天这篇文章我们来研究一下。

std::apply

std::apply最主要的作用就是把tuple转化为函数参数,然后去调用函数。如果没有STL,我们需要自行去实现,首先需要实现一个sequence,里面是从0到size,分别对应tuple中的每一个参数,然后再通过std::get和折叠展开语法来把tuple中的数据展开到参数列表中,比如这样:

template <int... Seq>
struct sequence {};template <int N, int... Seq>
struct make_seq : make_seq<N - 1, N - 1, Seq...> {};template <int... Seq>
struct make_seq<0, Seq...> {using type = sequence<Seq...>;
};template <typename Func, typename Tuple, int... Seq>
decltype(auto) Apply_detail(const Func &func, const Tuple &tu, const sequence<Seq...> &) {return func(std::get<Seq>(tu)...); // 参数展开
}template <typename Func, typename Tuple>
decltype(auto) Apply(const Func &func, const Tuple &tu) {return Apply_detail(func, tu, typename make_seq<std::tuple_size<Tuple>::value>::type());
}void Show(int a, double b, const std::string &c) {std::cout << a << " " << b << " " << c << std::endl;
}int main(int argc, const char *argv[]) {std::tuple tu(1, 2.5, "abc");Apply(Show, tu);return 0;
}

这个seq的生成可能有点难懂,简单来说,假如我们tuple有4个参数,那么我们要把make_seq<4>变成make_seq<3, 3>,再变成make_seq<2, 2, 3>,再变成make_seq<1, 1, 2, 3>,再变成make_seq<0, 0, 1, 2, 3>,当第一个参数是0的时候,后面不再变化,这时候正好可以得到从0到3的序列,然后用这个参数序列构造出sequence<0, 1, 2, 3>。然后,再利用这个序列,传给std::get来处理tuple
折贴语句func(std::get<Seq>(tu)...)就变成了func(std::get<0>(tu), std::get<1>(tu), std::get<2>(tu), std::get<3>(tu)),从而达成我们的目的。

而有了std::apply,它可以实现同样的功能,请看例子:

#include <iostream>int sum(int a, int b, int c) {return a + b + c;
}int main(int argc, const char *argv[]) {std::tuple tu(1, 2, 3);int res = std::apply(sum, std::move(tu));std::cout << res << std::endl; // 输出6return 0;
}

除了普通函数,apply的第一个参数还可以是函数指针、lambda、仿函数对象,不再赘述。
那么,对于非静态成员函数怎么办?很简单,把需要调用的对象作为tuple中的第一个成员即可,例如:

#include <iostream>
struct TT {int sum(int a, int b, int c) {return a + b + c;}
};int main(int argc, const char *argv[]) {std::tuple tu(TT(), 1, 2, 3); // 第一个成员就是调用成员int res = std::apply(&TT::sum, std::move(tu)); // 这里传成员函数指针// 效果相当于std::get<0>(tu).sum(1, 2, 3)std::cout << res << std::endl;return 0;
}

变参模板应用std::apply

那么,对于变参模板要怎么办?由于std::apply的第一个参数是一个可调用对象,因此,对于模板来说,必须要进行实例化。而对于变参模板来说,它本身存在意义就在于可以根据参数来自动推导出模板的类型,如果要手动实例化的话,就会失去意义,请看例程:

#include <iostream>
template <typename... T>
void test(T... arg) {(std::cout << ... << arg) << std::endl;
}int main(int argc, const char *argv[]) {std::tuple tu(1, 2.5, "abc");std::apply(test, std::move(tu)); // errstd::apply(test<int, double, const char *>, std::move(tu)); // 没意义return 0;
}

怎么办呢?其实,可以简单用lambda封装一下就好了,把变参目标的调用放在lambda当中,然后把类型推导的任务交给lambda本身即可,也就是auto大法好啦。请看例程:

#include <iostream>template <typename... T>
void test(T... arg) {(std::cout << ... << arg) << std::endl;
}int main(int argc, const char *argv[]) {std::tuple tu(1, 2.5, "abc");std::apply([](auto &&... args) {return test(args...);}, std::move(tu)); // 正常调用return 0;
}

std::make_from_tuple

std::apply可以解决函数调用时,tuple转参数列表,但是如果希望调用的是构造函数怎么办?构造函数毕竟没办法直接获取函数指针。
其中一种办法就是自行封装一层模板,例如:

class Test {public:Test(int a, double b, const std::string &c): a_(a), b_(b), c_(c) {}void show() const {std::cout << a_ << " " << b_ << " " << c_ << std::endl;}
private:int a_;double b_;std::string c_;
};// 自行封装构造过程
template <typename T, typename... Args>
T Create(Args &&...args) {return T(args...);
}int main(int argc, const char *argv[]) {std::tuple tu(1, 2.5, "abc");Test &&t = std::apply([](auto &&...args)->Test {return Create<Test>(args...);}, std::move(tu));t.show(); // 打印:1 2.5 abcreturn 0;
}

而STL提供的std::make_from_tuple就是同样的作用,我们可以直接用它来代替自行实现的Create函数:

#include <iostream>// Test实现同上,省略int main(int argc, const char *argv[]) {std::tuple tu(1, 2.5, "abc");Test &&t = std::make_from_tuple<Test>(std::move(tu));t.show();return 0;
}

C++17之std::apply与std::make_from_tuple相关推荐

  1. 【C++ 泛型编程 高级篇】 C++ 17 解析std::apply 的多种应用场景

    目录标题 1. 引言 1.1. C++17标准的引入 1.2. std::apply的基本概念 2. std::apply的基本用法 2.1. std::apply的函数签名 2.2. std::ap ...

  2. C++中std::function和std::bind

    1.可调用对象 可调用对象有一下几种定义: 是一个函数指针,参考 C++ 函数指针和函数类型: 是一个具有operator()成员函数的类的对象: 可被转换成函数指针的类对象: 一个类成员函数指针: ...

  3. std::future和std::promise和std::packaged_task

    std::future 其实future有两个兄弟,一个是std::future, 一个是它大哥std::shared_future.他们的区别就是std::future只支持移动语义,它所引用的共享 ...

  4. [C/C++]关于C++11中的std::move和std::forward

    http://blog.sina.com.cn/s/blog_53b7ddf00101p5t0.html std::move是一个用于提示优化的函数,过去的c++98中,由于无法将作为右值的临时变量从 ...

  5. c++11 std::bind与std::function

     function模板类和bind模板函数,使用它们可以实现类似函数指针的功能,但却比函数指针更加灵活,特别是函数指向类的非静态成员函数时. std::function可以绑定到全局函数/类静态成 ...

  6. C++的std::is_same与std::decay

    一.背景 有一个模板函数,函数在处理int型和double型时需要进行特殊的处理,那么怎么在编译期知道传入的参数的数据类型是int型还是double型呢? #include <iostream& ...

  7. C++——std::async和std::thread

    作者:小 琛 欢迎转载,请标明出处 参考文章: 傻月菇凉博主-博客 OceanStar的学习笔记博主-博客 NGC_2070博主-博客 文章目录 std::thread thread的提出 使用方法. ...

  8. 【C++ 泛型编程 入门篇】C++元模版中std::remove_reference_t和std::remove_cv_t的运用

    目录标题 1. std::remove_reference_t和std::remove_cv_t简介 1.1 函数原型及基本解释 1.2 在C++11, C++14, C++17, C++20中的表现 ...

  9. C++ std::any、std::variant和std::optional的原位构造(In-Place Construction)

    本文翻译自 Bartlomiej Filipek 的博客文章 In-Place Construction for std::any, std::variant and std::optional,翻译 ...

  10. std::variant 与 std::visit

    std::variant 简介 std::variant 是c++17 引入的一个类型,其作用类似于C语言中的Union,但是比Union 的功能强大的多. C语言中一个联合体Union 可以储存多种 ...

最新文章

  1. mysql数据库隐式表_详解MySQL数据库常见的索引问题:无索引,隐式转换,附实例说明...
  2. svn提交怎么全选_做外贸怎么精准开发国外客户?终于有答案了
  3. C++ vector多维数组初始化及清零
  4. css 浮动问题 display显示 和 光标设置cursor
  5. jzoj4208-线段树什么的最讨厌了【dfs】
  6. java调用scilab_Java调用Scilab-编译运行Javasci v2
  7. iOS CoreData简单入门 - Swift版
  8. 用 Java 开发自己的 Kubernetes 控制器,想试试吗?
  9. 多线程下单例模式:懒加载(延迟加载)和即时加载
  10. STM32标准库官网下载方法
  11. 【优化算法】爬虫搜索算法(RSA)【含Matlab源码 1838期】
  12. 转正述职报告 实习转正 工作汇报 述职模板免费下载_PPTX图片设计素材_包图网888pic.com...
  13. 在资源管理器中不小心关掉了什么,win10桌面不见了,变黑了
  14. java路径的上一级_java路径两种写法/和\\以及 ./和../以及/之间的区别?
  15. ⚓写写5G网速及页面提速中的延迟加载Lazyloading
  16. 【转】Windows Linux MacOS操作系统的区别
  17. day fit into much one too_PGone Talking too much歌词
  18. 磁盘IOPS概念及IOPS的计算与测试
  19. item_search_img-按图搜索1688商品(拍立淘)接口的接入参数说明
  20. 分省增值税和营业税数据(2009-2019年)

热门文章

  1. python对气象工作有没有用_Python语言在气象资料下载中的应用
  2. 联通UPhone计划是国家重大专项?
  3. arcpy批量合并融合矢量数据
  4. BLE蓝牙的配对过程浅析
  5. ADO访问Excel 2007
  6. 计算机求职信英语作文模板,英语自荐信范文(计算机)
  7. php网站视频播放外链,用视频网站来做外链只需三步
  8. ZCMU-1345: 国际象棋
  9. windows打开linux的vmdk,如何在VirtualBox中打开VMDK文件
  10. 如何使用Xcode的Targets来管理开发和生产版本的构建