##变参模板Variadic Templates
变参模板(Variadic Templates)顾名思义就是参数类型和个数可以改变的模板。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| template<typename... Arguments> class VariadicTemplate;
VariadicTemplate<double, float> instance; VariadicTemplate<bool, unsigned short int, long> instance; VariadicTemplate<char, std::vector<int>, std::string, std::string, std::vector<long long>> instance;
VariadicTemplate<> instance;
template<typename... Arguments> void SampleFunction(Arguments... parameters);
SampleFunction<int, int>(16, 24); SampleFunction<std::string>("fun");
|
有人要问了这个省略号C语言里就有嘛,printf不就是不定参数的嘛。但是…但是,变参模板是类型安全的,而且它可以让类似功能的实现得到极大简化。这篇文章就介绍了一个用变参模板实现的非常精巧的类型安全的printf, 还简要的说明了C++11引入这个特性的动机。我对模板元编程不甚了解,但是从大牛们用奇淫技巧实现的boost::mpl和boost::tuple来模拟可变参数模板,不难看出这个功能对编写C++库的重要性。当然如果Concepts不被移出C++11标准,C++泛型能力会有翻天覆地的提高,不管怎样,C++11在语言层级增加了对变参模板支持,还是极大的增强了C++模板的抽象能力。
##std::tuple
对于大多数程序员来说可能很少去编写模板库,但是新的可变参数的容器std::tuple大多数都会用到。tuple就是一个包含任意多个不同类型的数据成员的集合,就像一个增强版的std::pair。直接贴出一些用例,细节参照手册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| #include <iostream> // std::cout #include <tuple> // std::tuple, std::get, std::tie, std::ignore
int main () { std::tuple<int,char> foo (10,'x'); auto bar = std::make_tuple ("test", 3.1, 14, 'y');
std::get<2>(bar) = 100;
int myint; char mychar;
std::tie (myint, mychar) = foo; std::tie (std::ignore, std::ignore, myint, mychar) = bar;
mychar = std::get<3>(bar);
std::get<0>(foo) = std::get<2>(bar); std::get<1>(foo) = mychar;
std::cout << "foo contains: "; std::cout << std::get<0>(foo) << ' '; std::cout << std::get<1>(foo) << '\n';
return 0;
|
##实际中应用
虽然变参模板无比拉风,但是平时编程时却不容易用到,tuple灵活强大,但是只用在函数返回值上,也未免大财小用,而且用多了会降低代码的可读性。在项目中,有需求要测试触发signal的功能,需要一个slot的mock,signal 可能传递不同个数不同类型的参数,终于”以权谋私”用上了Variadic Templates和tuple。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| template<typename... Args> class SignalReceiverMock { public: SignalReceiverMock() : _slotCalled(false) {}
void slot(Args... args) { _slotCalled = true; _arguments = std::make_tuple(args...); }
bool slotCalled() { return _slotCalled; }
bool argumentsPassedCorrectly(Args... args) { auto arguments = std::make_tuple(args...); return arguments == _arguments; }
private: bool _slotCalled; std::tuple<Args...> _arguments; };
SignalReceiverMock<std::string, int> receiver1; SignalReceiverMock<> receiver2; Signal1 signal1("test", 0); Signal2 signal2(); signal1.connect(boost::bind(&SignalReceiverMock<std::string, int>::slot, *receiver1, _1, _2)); signal2.connect(boost::bind(&SignalReceiverMock<>::slot, *receiver2)); .... testResult.asserTrue(receiver1.slotCalled() && receiver1.argumentsPassedCorrectly("test", 0)); testResult.asserTrue(receiver2.slotCalled());
|