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), 0)...};
}

  写法2比较巧妙,借助初始化列表和逗号表达式来展开,在生成初始化列表的过程中展开参数包。这种方式不够间接,我希望能把printarg函数干掉,于是第三种方法出来了:

//----写法3
template<typename... Args>
void print3(Args... args)
{
    std::initializer_list<int>{([&]{cout << args << endl; }(), 0)...};
}

  写法3通过lambda表达式简化书写,非常间接直观,是我最喜欢的方式。那么除了这三种展开方式之外还有第4种吗?答案是肯定的。来看第4种写法:

//----写法4
template<std::size_t I = 0, 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 = 0, 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 + 1>(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-1>::print(t);
        cout << std::get<N - 1>(t) << endl;   //升序
    }
};

template <>
class printer<0> {
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 - 1, N - 1, Indexes...>{};

template<int... indexes>
struct MakeIndexes<0, 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; }(), 0)...};
}

template<typename ... Args>
void print6(Args... args)
{
    print_helper(typename MakeIndexes<sizeof... (Args)>::type(), std::make_tuple(args...));
}

  第6种写法略显复杂,但很有意义,它实际上是进行了一个“氧化还原”反应,先将参数包转换为tuple,接着又将tuple转换为参数包,很有趣也很有用。至此,我们已经发现了6种展开可变模板参数的写法了,太棒了,你也许还兴犹未尽,还有第7种写法吗,有,但我已懒得再写了,其实还不止7种呢,那更多的写法在哪儿呢?在那儿,就在那儿等着你去发现……

时间: 2024-10-10 18:46:22

play with variadic template的相关文章

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

编译器对C++ 11变参模板(Variadic Template)的函数包扩展实现的差异 题目挺绕口的.C++ 11的好东西不算太多,但变参模板(Variadic Template)肯定是其中耀眼的一颗明星,在C++设计新思维中,你可以看到很多模版的代码为了支持不确定的参数个数,而要重载1个参数到N个模板参数的N个函数.虽然种代码一般也是用会用宏和脚步辅助生成.但我想也没有人愿意看到几千行这种单调的函数.通过这个东东,模板的威力可以爆发. 目前的最新的编译器基本都已经支持Variadic Tem

C++ 遍历可变模板参数 iterate variadic template arguments

1 template<size_t I = 0, typename FuncT, typename ...Tp> 2 inline typename std::enable_if_t<I == sizeof ...(Tp)> for_each(std::tuple<Tp ...>&, FuncT) 3 { 4 } 5 6 template<size_t I = 0, typename FuncT, typename ...Tp> 7 inline t

variadic template (2)

在上一部分中,完成了一个简单的使用可变模板函数printf1 的编写以及初步尝试,以下是我对可变模板类的理解以及初步使用记录. 上一部分中,可变模板函数是通过 不断地调用类似递归的操作来完成对 parameter pack 不断解包(我感觉拆分更贴切一点)实现. 而可变模板类 则是通过不断的继承自身,并不断实例化此模板参数的类定义,并提升为父类的方法实现.(表达的有点不清楚,可以跳过这一句直接看下面的描述) 可变模板类定义为: template<typename... Elements> cl

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

Introduction: Before the possibilities of the new C++ language standard, C++11, the use of templates was quite limited when it came to implementing for instance function objects (functors) & tuple facilities. Implementing these sort of things using e

Functions

1 Local static variables Local static variables are not destroyed when the function ends; they are destroyed when the program terminates. 2. Function parameter list Parameter names are optional. However, there is no way to use an unnamed parameter. A

第16章 模板与泛型编程

16.1.1函数模板 //template parameter list template<typename T1,typename T2> int compare(const T1& v1, const T2&v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; } When we call a function template, the compiler (ordinarily) uses

C++14使用std::integer_sequence展开tuple作为函数的参数

元组是一种长度固定的允许有不同类型元素的集合,根据元素的个数不同又分别称作一元组.二元组.三元组等.C++11中标准库增加了一个叫std::tuple的类模板,用于表示元组. 下面的代码演示了使用C++创建一个三元组. auto tuple = std::make_tuple(1, 'A', "test"); std::cout << std::get<0>(tuple) << std::endl; std::cout << std::g

LuaTinker的bug和缺陷

LuaTinker的bug和缺陷 LuaTinker是一套还不错的C++代码和Lua代码的绑定库,作者是韩国人Kwon-il Lee,作者应该是参考了LuaBind后,为了简化和避免过重而实现的.其官网在http://gpgstudy.com/gpgiki/LuaTinker ,但可惜全部是韩文的,而最新的代码可以在Git上下载,https://github.com/zupet/LuaTinker .对比LuaBind,LuaPlus这类库,其实现非常非常非常非常非常轻,大约只有1000多行,至

Chapter 1-02

Please indicate the source: http://blog.csdn.net/gaoxiangnumber1 Welcome to my github: https://github.com/gaoxiangnumber1 1.8 应用到 Observer 上 ?既然通过 weak_ptr 能探查对象的生死,那么 Observer 模式的竞态条件就很容易解决,只要让Observable 保存 weak_ptr 即可: ?前文代码 (3) 处(p. 10 的 L17)的竞态条件