play with variadic template
我曾在公司内部的一次技术培训课程中讲到如何展开可变模板参数的问题,具体来说是如何打印可变模板参数,我初略数了一下,有很多种,下面来看看到底有多少种不同的方法展开可变模板参数吧。
//展开变参的N种方法, 以print为例
//----写法1
template<typename T>
void print(T t)
{
cout << t << endl;
} template<typename T, typename... Args>
void print(T t, Args... args)
{
print(t);
print(args...);
}
写法1很普通,没什么特别的,算是中规中矩,也是一般情况下的展开方式,下面来看特殊一点的展开方式:
//----写法2
template<typename T>
void printarg(T t)
{
cout << t << endl;
} template<typename... Args>
void print2(Args... args)
{
//int a[] = { (printarg(args), 0)... };
std::initializer_list<int>{(printarg(args), )...};
}
写法2比较巧妙,借助初始化列表和逗号表达式来展开,在生成初始化列表的过程中展开参数包。这种方式不够间接,我希望能把printarg函数干掉,于是第三种方法出来了:
//----写法3
template<typename... Args>
void print3(Args... args)
{
std::initializer_list<int>{([&]{cout << args << endl; }(), )...};
}
template<typename... Args>
void expand(Args... args)
{
std::initializer_list<int>{(cout << args << endl, 0)...};
}
写法3通过lambda表达式简化书写,非常间接直观,是我最喜欢的方式。那么除了这三种展开方式之外还有第4种吗?答案是肯定的。来看第4种写法:
//----写法4
template<std::size_t I = , typename Tuple>
typename std::enable_if<I == std::tuple_size<Tuple>::value>::type printtp(Tuple t)
{
//cout << "at range" << endl;
} template<std::size_t I = , typename Tuple>
typename std::enable_if<I < std::tuple_size<Tuple>::value>::type printtp(Tuple t)
{
std::cout << std::get<I>(t) << std::endl;
printtp<I + >(t);
} template<typename... Args>
void print4(Args... args)
{
printtp(std::make_tuple(args...));
}
第4种写法也很巧妙,借助tuple和enable_if,通过不断地递增I来获取tuple中的元素并打印。至此已经完成4种写法了,还有没有第5种方法呢?答案还是肯定的。
//写法5
template <int N>
class printer {
public:
template<typename Tuple>
static void print(const Tuple& t)
{
//cout << std::get<N - 1>(t) << endl; //降序
printer<N->::print(t);
cout << std::get<N - >(t) << endl; //升序
}
}; template <>
class printer<> {
public:
template<typename Tuple>
static void print(const Tuple& t)
{
}
}; template<typename... Args>
void print5(const Args&... args)
{
auto tp = std::make_tuple(args...);
printer<sizeof...(args)>::print(tp);
}
写法5的实现思路和写法4有异曲同工之妙,都是借助于int模板参数的递增来实现递归,不同的是写法是通过类模版的特化来实现递归的。已经5种了,还有第6种吗?看,第6种写法来了:
//写法6
template<int...>
struct IndexTuple{}; template<int N, int... Indexes>
struct MakeIndexes : MakeIndexes<N - , N - , Indexes...>{}; template<int... indexes>
struct MakeIndexes<, indexes...>
{
typedef IndexTuple<indexes...> type;
}; template<int ... Indexes, typename ... Args>
void print_helper(IndexTuple<Indexes...>, std::tuple<Args...>&& tup)
{
std::initializer_list<int>{([&]{cout << std::get<Indexes>(tup) << endl; }(), )...};
} template<typename ... Args>
void print6(Args... args)
{
print_helper(typename MakeIndexes<sizeof... (Args)>::type(), std::make_tuple(args...));
}
第6种写法略显复杂,但很有意义,它实际上是进行了一个“氧化还原”反应,先将参数包转换为tuple,接着又将tuple转换为参数包,很有趣也很有用。至此,我们已经发现了6种展开可变模板参数的写法了,太棒了,你也许还兴犹未尽,还有第7种写法吗,有,但我已懒得再写了,其实还不止7种呢,那更多的写法在哪儿呢?在那儿,就在那儿等着你去发现……
/*****************更新***************/
将变参在展开过程中变为tuple<pair<...>>
#define MAKE_PAIR(text) std::pair<std::string, decltype(text)>{#text, text}
template<typename T, typename T1, typename... Args>
constexpr static inline auto apply(T const & t, const T1& first, const Args&... args)
{
return apply(std::tuple_cat(t, std::make_tuple(MAKE_PAIR(first))), args...);
}
template<typename T>
constexpr static inline auto apply(T const & args)
{
return args;
}
#define META(...) auto meta(){ return apply(std::tuple<>(), __VA_ARGS__); }
play with variadic template的更多相关文章
- 编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异
编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异 题目挺绕口的.C++ 11的好东西不算太多,但变参模板(Variadic Template)肯定是其中耀眼的一 ...
- C++11 : variadic templates(可变参数模板)
Introduction: Before the possibilities of the new C++ language standard, C++11, the use of templat ...
- c++11-17 模板核心知识(十二)—— 模板的模板参数 Template Template Parameters
概念 举例 模板的模板参数的参数匹配 Template Template Argument Matching 解决办法一 解决办法二 概念 一个模板的参数是模板类型. 举例 在c++11-17 模板核 ...
- 如何设计一门语言(八)——异步编程和CPS变换
关于这个话题,其实在(六)里面已经讨论了一半了.学过Haskell的都知道,这个世界上很多东西都可以用monad和comonad来把一些复杂的代码给抽象成简单的.一看就懂的形式.他们的区别,就像用js ...
- 实现一个 Variant
很多时候我们希望能够用一个变量来保存和操作不同类型的数据(比如解析文本创建 AST 时保存不同类型的结点),这种需求可以通过继承来满足,但继承意味着得使用指针或引用,除了麻烦和可能引起的效率问题,该做 ...
- c++ 相关的技术资源整理归类
最近一段时间 c++ 社区里最火热的话题莫过于 cppcon2015 了, isocpp 上一堆相关的新闻,其中有一个页面罗列了该会议的全部主题, 匆匆一瞥几乎眼花缭乱,为期一个星期的会议竟有上百个演 ...
- c++ 模板元编程的一点体会
趁着国庆长假快速翻了一遍传说中的.大名鼎鼎的 modern c++ design,钛合金狗眼顿时不保,已深深被其中各种模板奇技淫巧伤了身...论语言方面的深度,我看过的 c++ 书里大概只有 insi ...
- 类型安全且自动管理内存的返回 std::string 的 sprintf 实现
在这篇博文里,我提到了一个例子,说的是使用C++实现类型安全的printf.这个例子很惊艳,但是在我写程序的时候,并非那么"迫切"地需要它出现在我的工具箱中,因为它并不比普通的pr ...
- C++11的一些新特性
3.1.9崭新的Template特性 Variadic Template 可变参数模板 void print() { } template <typename T, typename… Type ...
随机推荐
- gnu screen的用法
在使用ssh或者telnet登录远程主机后执行一些耗时的命令, 如果此时ssh或者telnet中断, 那么远程主机上正在执行的程序或者说命令也会被迫终止. screen能够很好地解决这个问题, scr ...
- 最常用的Java库一览(13年的文章)
来源于:http://www.importnew.com/7530.html 本文由 ImportNew - 邢 敏 翻译自 programcreek.欢迎加入翻译小组.转载请见文末要求. 写在前面: ...
- iOS presentedViewController和presentingViewController的区别
当我们用present进行界面跳转时,会涉及到一个传向关系. 比如:A -> B 界面A跳转界面B A.presentedViewControlle = B B.presentingVie ...
- oracle安装后tnsnames.ora内容
# tnsnames.ora Network Configuration File: D:\Develop\oracle11g\product\11.2.0\dbhome_1\network\admi ...
- 像python一样运行js的__main__
在测试时,使用单文件进行简单测试既简洁又清晰.js其实也是可以做到的 if (require && require.main == module) { console.log(&quo ...
- hihocoder234周 计算不包含黑点的矩形个数
题目链接 问题描述 一个棋盘有n条横线,m条竖线,上面有k个黑点,问有多少个不包含黑点的矩形. 数据范围: n和m最大为1000,k最大为10 方法一:动态规划 复杂度n*m*k. import ja ...
- Centos 安装GIT 1.7.1
在Linux上安装Git 1.首先,你可以试着输入git,看看系统有没有安装Git: git 2.安装GIT https://git-scm.com/download/linux yum instal ...
- Mac Apache Tomcat 配置
1.配置准备工作 1)配置 Tomcat 准备工作 下载相关软件 apache-tomcat-9.0.6.zip tomcat 官网 Tomcat 配置软件下载地址,密码:sgrn. 2)配置注意事项 ...
- ext2文件系统 - mke2fs
上一遍博文的重点其实将ext2整体的组织框架,我们知道了ext2文件系统由块组组成,每个块组里面的组织形式.我们甚至直接把超级块和组描述符里面的内容,用十六进制形式展现了出来.这篇博文主要讲述如何 ...
- Python 爬虫 大量数据清洗 ---- sql语句优化
. 问题描述 在做爬虫的时候,数据量很大,大约有五百百万条数据,假设有个字段是conmany_name(拍卖公司名称),我们现在需要从五百万条数据里面查找出来五十家拍卖公司, 并且要求字段 time( ...