我曾在公司内部的一次技术培训课程中讲到如何展开可变模板参数的问题,具体来说是如何打印可变模板参数,我初略数了一下,有很多种,下面来看看到底有多少种不同的方法展开可变模板参数吧。

//展开变参的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的更多相关文章

  1. 编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异

    编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异 题目挺绕口的.C++ 11的好东西不算太多,但变参模板(Variadic Template)肯定是其中耀眼的一 ...

  2. C++11 : variadic templates(可变参数模板)

      Introduction: Before the possibilities of the new C++ language standard, C++11, the use of templat ...

  3. c++11-17 模板核心知识(十二)—— 模板的模板参数 Template Template Parameters

    概念 举例 模板的模板参数的参数匹配 Template Template Argument Matching 解决办法一 解决办法二 概念 一个模板的参数是模板类型. 举例 在c++11-17 模板核 ...

  4. 如何设计一门语言(八)——异步编程和CPS变换

    关于这个话题,其实在(六)里面已经讨论了一半了.学过Haskell的都知道,这个世界上很多东西都可以用monad和comonad来把一些复杂的代码给抽象成简单的.一看就懂的形式.他们的区别,就像用js ...

  5. 实现一个 Variant

    很多时候我们希望能够用一个变量来保存和操作不同类型的数据(比如解析文本创建 AST 时保存不同类型的结点),这种需求可以通过继承来满足,但继承意味着得使用指针或引用,除了麻烦和可能引起的效率问题,该做 ...

  6. c++ 相关的技术资源整理归类

    最近一段时间 c++ 社区里最火热的话题莫过于 cppcon2015 了, isocpp 上一堆相关的新闻,其中有一个页面罗列了该会议的全部主题, 匆匆一瞥几乎眼花缭乱,为期一个星期的会议竟有上百个演 ...

  7. c++ 模板元编程的一点体会

    趁着国庆长假快速翻了一遍传说中的.大名鼎鼎的 modern c++ design,钛合金狗眼顿时不保,已深深被其中各种模板奇技淫巧伤了身...论语言方面的深度,我看过的 c++ 书里大概只有 insi ...

  8. 类型安全且自动管理内存的返回 std::string 的 sprintf 实现

    在这篇博文里,我提到了一个例子,说的是使用C++实现类型安全的printf.这个例子很惊艳,但是在我写程序的时候,并非那么"迫切"地需要它出现在我的工具箱中,因为它并不比普通的pr ...

  9. C++11的一些新特性

    3.1.9崭新的Template特性 Variadic Template 可变参数模板 void print() { } template <typename T, typename… Type ...

随机推荐

  1. oracle初始化化表空间用户权限

    oracle单实例安装完毕,需要初始化表空间.用户.等信息.不积跬步,何以至千里! 用sys账户登录oracle数据库,以此来完成所有操作! 01.创建临时表空间 create temporary t ...

  2. View类的XML属性、相关方法及说明

    XML属性 相关方法 说明 android:alpha setAlpha(float) 设置该组件的透明度 android:background setBackgroundResource(int) ...

  3. Netty-gRPC介绍和使用

    转自:http://www.saily.top/2017/07/23/netty5/ gRPC Define your service using Protocol Buffers, a powerf ...

  4. JavaScript RegExp Object 正则表达式入门

    什么是 RegExp? RegExp 是regular expression的缩写. RegExp 对象表示正则表达式,它是对字符串执行模式匹配的强大工具. 当您检索某个文本时,可以使用一种模式来描述 ...

  5. [转]nf_conntrack: table full, dropping packet 连接跟踪表已满,开始丢包 的解决办法

      nf_conntrack: table full, dropping packet  连接跟踪表已满,开始丢包 的解决办法 中午业务说机器不能登录,我通过USM管理界面登录单板的时候发现机器没有僵 ...

  6. Linux基本数据类型大小——int,char,long int,long long int

    转自:http://paddy-w.iteye.com/blog/1403217 在Linux操作系统下使用GCC进行编程,目前一般的处理器为32位字宽,下面是/usr/include/limit.h ...

  7. Socket编程:之TCP案例

    转载请加上博文引用:http://i.cnblogs.com/EditPosts.aspx?postid=5733248&update=1 服务端: #include <stdio.h& ...

  8. HDU 4585 Shaolin (STL)

    Shaolin Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)Total Sub ...

  9. window 10 企业版激活

    一. 用管理员权限打开CMD.EXE 接着输入以下命令: slmgr /ipk NPPR9-FWDCX-D2C8J-H872K-2YT43 弹出窗口提示:“成功的安装了产品密钥”. 继续输入以下命令: ...

  10. SharePoint 2013 创建Web Application

    今天继续SharePoint 2013 的探索之旅,之前几篇文章分析了SharePoint 2013的物理拓扑结构,安装,以及逻辑体系结构.在这篇文章中,我将继续Step By Step形式演示如何在 ...