首页 > 程序人生 > 漫话C++0x(六)—- variadic templates

漫话C++0x(六)—- variadic templates

2011年11月20日 发表评论 阅读评论

熟悉printf/scanf的朋友应该对C/C++中的变参函数不陌生,它可以支持任意个数的参数。模板(template)在C++中的地位相信不用我再多说,但是,一直以来,C++模板不支持变参,这成了模板一个被诟病的地方,在很多使用场景限制了模板的威力。举例来说,同样语义的模板函数,因为参数个数不定,有若干个,这样的场景之下,我们只能通过枚举的方式来实现。这样的方法,一方面使得代码本身不够简洁,另一方面因为枚举只能做到有限个数的参数,还是在一定程度上限制了使用的灵活性。现在,C++0x引入了变参模板,上面讲的问题便迎刃而解了,这对C++程序员朋友们来说是莫大的福音。下面我们先看一个简单的例子,先对变参模板有一个直观的印象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include < iostream>
#include < string>
 
void Print()
{
    std::cout < < "\n";
}
 
template< typename T, typename ... TRest>
void Print(const T& obj, const TRest& ... rest)
{
    std::cout < < obj << " ";
    Print(rest ...);
}
 
int main()
{
    double p = 3.14;
    std::string s("pi");
    Print(p, &p, s, &s);
}


下面是上述例子运行的情况:

1
2
3
wuzesheng@ubuntu:~/work/c++0x$ g++ variadic_function_templates.cc -std=c++0x -o variadic_test
wuzesheng@ubuntu:~/work/c++0x$ ./variadic_test 
3.14 0xbfed56e8 pi 0xbfed56f8

上面的例子应该比较容易看懂,实现了一个变参数的Print()函数,不知道大家有没发现,上面的实现有点递归的味道,第一个没有参数的版本,相当于一个初始值,接下来的变参模板版本相关的通用的递归过程。另外,在编译的时候要加上-std=c++0x,要不然编译会有问题,这点需要大家注意一下。

相信通过上面的例子,大家对变参模板应该已经有了一个初步的印象。下面我们来看看变参模板使用过程中的一些需要注意的点。首先需要讲一个基本的概念—-Parameter Pack(参数包),它是理解变参模板的基础,通俗地讲,它指的就是变参模板中用来表示可变参数的实体。具体说来,它包括两种基本类型,一种是Template Parameter Pack,另一种是Function Parameter Pack。Template Parameter Pack是可以接受多个模板参数的Parameter Pack, Function Parameter Pack是可以接受多个函数参数的Parameter Pack。如下面的例子所示, 其中Types为Template Parameter Pack, args为Function Parameter Pack:

1
2
3
4
5
6
7
8
9
template< typename ... Types > struct Tuple;
Tuple<> t0;            // Types contains no arguments
Tuple< int> t1;        // Types contains 1 argument
Tuple< int, float> t2; // Types contains 2 arguments
 
template< typename ... Types > void Func(Types ... args);
Func();       // args contains no arguments
Func(1);      // args contains 1 argument
Func(2, 1.0); // args contains 2 argument

理解了变参模板的Parameter Pack,接下来我们来看看,如何把Parameter Pack展开,以为我们所用,这部分内容也是变参模板最为核心的内容。先看下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
template< typename ... Types > f(Types ... args);
 
template< typename ... Types > g(Types ... args)
{
    // "&args ..." is a pack expansion
    // "&args" is the pattern
    f(&args ...);
}
 
int a = 10;
std::string s("hello");
g(a, s) ==> f(&a, &s);

上面的例子中,提到两个概念,一个是“pack expansion”,即参数包的展开;一个是”pattern”,即展开时的模式。在变参模板中,参数在实例化的时候被展开,展开按指定的pattern方式进行。如上面的例子中所示,g的实现中调用了f, 在这里传给f的参数是传给g的每个参数取地址的结果。相信到这里,大家对Parameter Pack的展开,及其所用的Pattern已经有了一个初步的认识。在各种不同场景下,Parameter Pack展开所采用的Pattern是不一样的,下面是C++0x的文档提供的一些场景所对应的Pattern,在这里列出来,帮助大家更好的认识变参模板Parameter Pack的展开:

  • — In an initializer-list (8.5); the pattern is an initializer-clause.
  • — In a base-specifier-list (Clause 10); the pattern is a base-specifier.
  • — In a mem-initializer-list (12.6.2); the pattern is a mem-initializer.
  • — In a template-argument-list (14.3); the pattern is a template-argument.
  • — In a dynamic-exception-specification (15.4); the pattern is a type-id.
  • — In an attribute-list (7.6.1); the pattern is an attribute.
  • — In an alignment-specifier (7.6.2); the pattern is the alignment-specifier without the ellipsis.
  • — In a capture-list (5.1.2); the pattern is a capture.
  1. 2011年12月20日13:32 | #1

    好久没来了,这个不错哈~~等搞完毕设我也去仔细读读。读了以前几篇也很不错,继续努力哈。可以写的再细点,以后出个c++漫谈的书吧,哈哈

  2. 2011年12月20日22:52 | #2

    呵呵,最近有点忙,更新有点少了~ 后面尽量多写写,你也多写写~@Leon

  3. 2011年12月23日17:08 | #3

    同意博主的看法

  1. 本文目前尚无任何 trackbacks 和 pingbacks.
您必须在 登录 后才能发布评论.