Variadic templates

本文深入探讨了C++11中的可变参数模板特性,包括如何使用递归调用来实现打印多个不同类型的参数及求最大值,展示了如何利用类模板实现递归继承与合成,并提供了一个打印标准库tuple的实例。

Variadic Templates

可变参数模板,函数的参数可以有任意多个,参数的类型任意

1. 实现函数递归调用

实现打印任意多个不同类型参数

#include <iostream>
using namespace std;

//无参数版本
void print() {}

//n+1 个参数版本 
template <class T, class... Types>
void print(const T& firstArg, const Types&... args) {
    cout << firstArg << endl;
    print(args...);         //n个参数版本 
}

int main() {
    print(100, 4.533, "hello world!");
    return 0;
}

输出结果为
100
4.533
hello world!

在VS中单步跟踪,我发现编译器将此函数特化为三个版本
分别为:
1. void print(int, double, char *)
2. void print(dobule, char *)
3. void print(char *)
我们自己实现的无参版本 void print()
在函数调用过程中,此函数实现了递归调用,1调用2, 2调用3,3调用我们自己实现的无参版本
然后再返回3,2,1

实现任意多个参数的max

#include <iostream>
#include <algorithm>
using namespace std;

//特化,分解到只有一个参数时返回n
template<class T>
T maximum(T n) {
    return n;
}

template<class T, class... Args>
T maximum(T n, Args... args) {
    return std::max(n, maximum(args...));
}

int main() {
    cout << maximum(1,4,8,500,67,25,7,7,5,634) << endl;     //634
    cout << maximum(1.6,78.7,346.64) << endl;               //346.64
    return 0;
} 

2. 实现类的递归继承

#include <iostream>
#include <string>

//声明
template<class... Values> class tuple;
//无参版本,空类 
template<> class tuple<> {};

template<class Head, class... Tail>
class tuple<Head, Tail...> : private tuple<Tail...> {
    typedef tuple<Tail...> inherited;
public:
    tuple() {}
    //调用父类构造函数
    tuple(Head x, Tail... vtail) :_head(x), inherited(vtail...) {}
    Head head() {
        return _head;
    }
    inherited& tail() {
        return *this;
    }
private:
    Head _head;
};

int main() {
    tuple<int, double, std::string> t(1, 35.7, "Hello World!");
    std::cout << t.head() << std::endl;                 //输出1
    std::cout << t.tail().head() << std::endl;          //35.7
    std::cout << t.tail().tail().head() << std::endl;   //Hello World!
}

一个参数的tuple继承空类tuple,两个参数的继承一个参数的,三个参数的继承两个参数的……

3. 实现类的递归合成

#include <iostream>
#include <string>

//声明
template<class... Values> class tuple;
//无参版本,空类 
template<> class tuple<> {};

template<class Head, class... Tail>
class tuple<Head, Tail...> : private tuple<Tail...> {
    typedef tuple<Tail...> composed;
public:
    tuple() {}
    //调用父类构造函数
    tuple(Head x, Tail... vtail) :_head(x), _tail(vtail...) {}
    Head head() {
        return _head;
    }
    composed& tail() {
        return _tail;
    }
private:
    Head _head;
    composed _tail;
};

int main() {
    tuple<int, double, std::string> t(1, 35.7, "Hello World!");
    std::cout << t.head() << std::endl;                 //输出1
    std::cout << t.tail().head() << std::endl;          //35.7
    std::cout << t.tail().tail().head() << std::endl;   //Hello World!
}

一个参数的tuple含有空类,两个参数的tuple含有一个参数的tupe,三个参数的含有两个参数的……

下面我们借助类模板实现一个打印tuple的函数
对于tuple<int, double, std::string> t(1, 35.7, "Hello World!")
我们希望输出的格式为[1,35.7,Hello World!]
这里采用标准库的tuple 因为里面实现了函数get

#include <iostream>
#include <tuple>
#include <string>
using namespace std;

template <int INDEX, int MAX, class... Args>
struct print_tuple {
    static void print(ostream& os, const tuple<Args...>& t) {
        os << get<INDEX>(t) << (INDEX + 1 == MAX ? "" : ",");
        print_tuple<INDEX + 1, MAX, Args...>::print(os, t);
    }
};

//偏特化,当INDEX 等于 MAX时什么都不做
template <int MAX, class... Args>
struct print_tuple<MAX, MAX, Args...> {
    static void print(ostream& os, const tuple<Args...>& t) {}
};

//重载操作符 <<
template <class... Args>
ostream& operator<< (ostream& os, const tuple<Args...>& t) {
    os << "[";
    print_tuple<0, sizeof...(Args), Args...>::print(os, t);
    os << "]";
    return os;
}

int main(){
    tuple<int, double, string> t(100, 23.23, "Hello World");
    cout << t << endl;
}

输出[100,23.23,Hello World]

我最初是想借助函数模板用这样的方式输出的

template <class... Args>
ostream& operator<< (ostream& os, const tuple<Args...>& t) {
    os << "[";
    int size = sizeof...(Args);
    for (int i = 0; i < size; ++i)
        os << get<i>(t) << (i == size - 1 ? "" : ",");
    os << "]";
    return os;
}

但是编译肯定不能通过,爆出成吨的errors,因为对于函数get<T>(t),模板参数T必须是一个常量,要在编译期就能确定

4. 小结

variadic templates为c++11的重量级特性,能解决很多问题,在库的实现中有着广泛的应用,比如模板元编程黑魔法-.-,掌握了大概,离高手级的应用还差很远很远

35 0 C:\Program Files (x86)\Dev-Cpp\MinGW64\lib\gcc\x86_64-w64-mingw32\4.9.2\include\c++\array In file included from C:/Program Files (x86)/Dev-Cpp/MinGW64/lib/gcc/x86_64-w64-mingw32/4.9.2/include/c++/array 2 D:\范铭博编程文件\特殊\牛逼的东西.cpp from D:\范铭博编程文件\特殊\牛逼的东西.cpp 32 2 C:\Program Files (x86)\Dev-Cpp\MinGW64\lib\gcc\x86_64-w64-mingw32\4.9.2\include\c++\bits\c++0x_warning.h [Error] #error This file requires compiler and library support for the ISO C++ 2011 standard. This support is currently experimental, and must be enabled with the -std=c++11 or -std=gnu++11 compiler options. 8 1 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] 'constexpr' does not name a type 8 1 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] C++11 'constexpr' only available with -std=c++11 or -std=gnu++11 D:\范铭博编程文件\特殊\牛逼的东西.cpp In function 'int main()': 26 5 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] 'constexpr' was not declared in this scope 30 14 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] 'function' in namespace 'std' does not name a template type 31 30 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] expected ')' before '<' token D:\范铭博编程文件\特殊\牛逼的东西.cpp In destructor 'main()::Printer::~Printer()': 32 29 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] 'action' was not declared in this scope D:\范铭博编程文件\特殊\牛逼的东西.cpp In lambda function: 39 35 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Warning] extended initializer lists only available with -std=c++11 or -std=gnu++11 D:\范铭博编程文件\特殊\牛逼的东西.cpp In function 'int main()': 42 5 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Warning] lambda expressions only available with -std=c++11 or -std=gnu++11 42 6 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] no matching function for call to 'main()::Printer::Printer(main()::<lambda()>)' 42 6 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] candidates are: 29 12 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] main()::Printer::Printer() 29 12 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] candidate expects 0 arguments, 1 provided 29 12 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] main()::Printer::Printer(const main()::Printer&) 29 12 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] no known conversion for argument 1 from 'main()::<lambda()>' to 'const main()::Printer&' 45 7 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] 'struct main()::Printer' has no member named 'action' 48 16 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Warning] variadic templates only available with -std=c++11 or -std=gnu++11 48 16 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] expansion pattern 'int' contains no argument packs D:\范铭博编程文件\特殊\牛逼的东西.cpp In lambda function: 49 9 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] 'array' is not a member of 'std' 50 19 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] 'arr' was not declared in this scope 52 19 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] 'x' does not name a type 53 9 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] expected ';' before 'std' 54 5 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] expected primary-expression before '}' token 54 5 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] expected ')' before '}' token 54 5 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] expected primary-expression before '}' token D:\范铭博编程文件\特殊\牛逼的东西.cpp In function 'int main()': 54 5 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Warning] lambda expressions only available with -std=c++11 or -std=gnu++11 54 20 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Error] no match for call to '(main()::<lambda()>) (int, int, int, int, int)' 48 6 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] candidates are: 54 20 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] void (*)() <conversion> 54 20 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] candidate expects 1 argument, 6 provided 48 17 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] main()::<lambda()> 48 17 D:\范铭博编程文件\特殊\牛逼的东西.cpp [Note] candidate expects 0 arguments, 5 provided
最新发布
10-05
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值