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

//展开变参的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. MAVEN 搭建APPFUSE

    2010-05-21  利用Maven构建appfuse. 步骤如下: 1)Maven下载,下载apache-maven-2.2.1-bin.zip 下载地址:http://apache.freela ...

  2. [转载]RHEL-6启动时提示:“/usr/libexec/gconf-sanit

    原文地址:RHEL-6启动时提示:"/usr/libexec/gconf-sanity-check-2 exited with status 256"作者:huage 系统环境:R ...

  3. Deep Introduction to Go Interfaces.

    Standard Interface Intro Go’s interfaces are one of it’s best features, but they’re also one of the ...

  4. keras中的模型保存和加载

    tensorflow中的模型常常是protobuf格式,这种格式既可以是二进制也可以是文本.keras模型保存和加载与tensorflow不同,keras中的模型保存和加载往往是保存成hdf5格式. ...

  5. Spring异常解决 java.lang.NullPointerException,配置spring管理hibernate时出错

    @Repository public class SysUerCDAO { @Autowired private Hibernate_Credit hibernate_credit; /** * 根据 ...

  6. WKWebView 使用及注意事项

    iOS8之后,苹果推出了WebKit这个框架,用来替换原有的UIWebView,新的控件优点多多.由于一直在适配iOS7,就没有去替换,现在仍掉了iOS7,以为很简单的就替换过来了,然而在替换的过程中 ...

  7. 什么是分表和分区 MySql数据库分区和分表方法

    1.为什么要分表和分区 日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性 ...

  8. 自由是有代价的:聊聊这几年尝试的道路 要想生活好,别看哲学书和思想书。简单看看可以,看多了问题就大了。还是要去研究研究些具体的问题。别jb坐在屋子里,嘴里念着海子的诗,脑袋里想康德想的事情,兜里屁都没有,幻想自己是大国总理,去想影帝是怎么炼成的。

    自由是有代价的:聊聊这几年尝试的道路 现在不愿意写过多的技术文章了,一点是现在做的技术比较偏,写出来看的人也不多,二来是家庭事务比较繁多,没以前那么有时间写了.最近,园子里多了一些写经历的文章,我也将 ...

  9. 颜色传感器TCS230及颜色识别电路(转)

    摘要 TCS230是美国TAOS公司生产的一种可编程彩色光到频率的传感器.该传感器具有分辨率高.可编程的颜色选择与输出定标.单电源供电等特点:输出为数字量,可直接与微处理器连接.文中主要介绍TCS23 ...

  10. Postgresql 正则表达式

    在postgresql中使用正则表达式时需要使用关键字“~”,以表示该关键字之前的内容需匹配之后的正则表达式,若匹配规则不需要区分大小写,可以使用组合关键字“~*”: 相反,若需要查询不匹配这则表达式 ...