关于C++ 非类型模板以及unnamed namespace的一些疑问

  最近对C++ template的实例化过程很感兴趣,随着研究的深入进行,发现了一些比较有趣,比较不引人注意的特性,就是模板与linkage之间的联系。

  模板不用解释,基本稍微了解点C++的coder们,都知道模板的概念以及简单的应用。

 

一. 关于linkage(链接属性)

  所谓linkage,C++标准中,有这么一段话:

  A name is said to have linkage when it might denote the same object, reference, function, type, template,
  namespace or value as a name introduced by a declaration in another scope:
    — When a name has external linkage , the entity it denotes can be referred to by names from scopes of
     other translation units or from other scopes of the same translation unit.
    — When a name has internal linkage , the entity it denotes can be referred to by names from other scopes
       in the same translation unit.
    — When a name has no linkage , the entity it denotes cannot be referred to by names from other scopes.

  上面这段话的意思是,在C++中,当一个名字与另一个由其他作用域声明的名字,表示的是同一个对象、引用、函数、类型、模板、名字空间或者值,那么该名字就是有linkage(链接属性)的。

    ——如果该名字拥有external linkage,那么它所表示的实体就可以通过该名字,被来自其他编译单元(translation unit)的作用域,或者来自本编译单元的其他作用域引用。

    ——如果该名字拥有internal linkage,那么该名字可以被来自本编译单元的其他作用域引用,不能被来自其他编译单元的作用域引用。

    ——如果一个名字没有linkage,那么它不能被其他作用域引用。

 

二. template与static与linkage

  下面介绍一下,template的一些特别的限制。

  C++模板的参数,可以是类型,也可以是非类型。对于非类型参数,在《C++ templates》里有这么一段话:

  Note that nontype template parameters carry some restrictions. In general, they may be constant integral values (including enumerations) or pointers to objects with external linkage.

  Floating-point numbers and class-type objects are not allowed as nontype template parameters.

  意思就是,传递给非类型模板的实参,可以是常整数(包括枚举类型),或者拥有external链接属性的指向对象的指针。浮点数类型,与类类型是不允许做为非类型模板的形参的。例如:

 1 template <typename std::string obj>
 2 void func1()
 3 {
 4     // ...
 5 }
 6 
 7 template <typename double d>
 8 void func2()
 9 {
10     // ...
11 }

  以上两个例子都是错误的。第一个用了类类型std::string的对象作为template parameter,而第二个用double类型的变量作为template parameter。

  这里说点题外话,在计算机英语中,parameter 与 argument都表示参数,但一般来说,parameter表示的是函数声明里的参数(即形参),而argument表示的是,调用函数是,传入的参数(即实参)。

  不过在C++ 11标准中,对于非类型模板参数的说明,有些许不同(也可能是本人理解有误吧)。下面是标准中关于非类型模板参数的说明:

  A template-argument for a non-type, non-template template-parameter shall be one of:

    — an integral constant expression (including a constant expression of literal class type that can be used
        as an integral constant expression as described in 5.19); or
    — the name of a non-type template-parameter; or
    — a constant expression (5.19) that designates the address of an object with static storage duration and
        external or internal linkage or a function with external or internal linkage, including function templates
      and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as
      & id-expression, except that the & may be omitted if the name refers to a function or array and shall
      be omitted if the corresponding template-parameter is a reference; or
    — a constant expression that evaluates to a null pointer value (4.10); or
    — a constant expression that evaluates to a null member pointer value (4.11); or

    — a pointer to member expressed as described in 5.3.1.

  接下来就是一些关于non-type template的限制:

  Note: A string literal (2.14.5) does not satisfy the requirements of any of these categories and thus is not

  an acceptable template-argument.

  Note: Addresses of array elements and names or addresses of non-static class members are not acceptable

  template-arguments.

  Note: Temporaries, unnamed lvalues, and named lvalues that do not have external linkage are not acceptable

  template-arguments when the corresponding template-parameter has reference type.

  本人英语不太好,上面的标准每怎么看太明白(标准里的用语实在是太拗口了,而且C++标准中,这样的用语方式到处都是),但是,大概还是看出上面并没有关于pointers to objects必须有external linkage的规定,倒是有关于当template parameter为类型的引用时,传递的argument必须有external linkage的规定。那么,我们看看时间的应用情况:

 1 #include <iostream>
 2 
 3 // a non-type template function
 4 template <char const *p>
 5 void foo()
 6 {
 7     std::cout << "foo: " << p << std::endl;
 8 }
 9 
10 static char a = 'a'; // internal linkage
11 char b = 'b'; // default linkage: external
12 
13 int main()
14 {
15     foo<&a>(); // error occurs while compiling this line
16     foo<&b>();
17 
18     return 0;
19 }

  以上的代码无法编译通过,原因就在于foo<&a>这句话。编译错误为:error C2970: “foo”: 模板参数“p”:“a”: 涉及带有内部链接的对象的表达式不能用作非类型参数。

  上面便是我在研究C++模板时,发现的一点疑问。

  后来我回头看看C++ 03标准,发现这么一段话:

  A template-argument for a non-type, non-template template-parameter shall be one of:
    — an integral constant-expression of integral or enumeration type; or
    — the name of a non-type template-parameter; or
    — the address of an object or function with external linkage, including function templates and function
      template-ids but excluding non-static class members, expressed as & id-expression where the & is
      optional if the name refers to a function or array, or if the corresponding template-parameter is a reference;
      or
    — a pointer to member expressed as described in 5.3.1 .

  上面就很清楚的写出了,非类型参数模板的参数,如果为指向对象的指针时,该指针必须有external linkage。

  故结论是,根据C++ 11标准,指向static变量的指针,也可以作为非类型参数模板的参数,但是C++ 03标准以及更早的标准中,就不行。

 

三. 关于unnamed namespace的疑问

  什么是unnamed namespace?其实就是在定义一个namespace时,不为该namespace添加名字。比如:

1 namespace {
2     int a;
3     void func()
4     {
5     }
6 } // end of unnamed namespace

  在编译时,C++编译器会为unnamed namespace添加一个unique name,并且在namespace外部添加一个using unique-name-of-this-unnamed-namespace;,这样就可以访问该namespace中的成员了。

  在C++ 11标准中,有这么一段话:

  An unnamed namespace or a namespace declared directly or indirectly within an unnamed namespace has

  internal linkage. All other namespaces have external linkage. A name having namespace scope that has not
  been given internal linkage above has the same linkage as the enclosing namespace if it is the name of
    — a variable; or
    — a function; or
    — a named class (Clause 9), or an unnamed class defined in a typedef declaration in which the class has
        the typedef name for linkage purposes (7.1.3); or
    — a named enumeration (7.2), or an unnamed enumeration defined in a typedef declaration in which the
        enumeration has the typedef name for linkage purposes (7.1.3); or
    — an enumerator belonging to an enumeration with linkage; or
    — a template.

  上面的话中,说明unnamed namespace有internal连接属性。并且在其中的变量,函数等,与unnamed namespace具有相同的linkage。

  但是在C++ 03标准中:  

  A name having namespace scope has external linkage if it is the name of
    — an object or reference, unless it has internal linkage; or
    — a function, unless it has internal linkage; or
    — a named class (clause 9), or an unnamed class defined in a typedef declaration in which the class has the
      typedef name for linkage purposes (7.1.3); or
    — a named enumeration (7.2), or an unnamed enumeration defined in a typedef declaration in which the
      enumeration has the typedef name for linkage purposes (7.1.3); or
    — an enumerator belonging to an enumeration with external linkage; or
    — a template, unless it is a function template that has internal linkage (clause 14); or
    — a namespace (7.3), unless it is declared within an unnamed namespace.

  重点是最后一句话,这句话表明了,在unnamed namespace中,除非你显示的说明你的变量或者函数有internal linkage,否则的话,就拥有默认的external linkage。

  网上有很多文章,上面直接就说,unnamed namespace内的变量,函数等,具有的是内部链接属性,其论据就是,用unnamed namespace囊括变量与函数,可以到达与static修饰变量与函数相同的功能,但是实际情况呢?下面以一段代码,证明上面那种不负责任的言论的荒谬性:

 1 #include <iostream>
 2 
 3 // a non-type template function
 4 template <char const *p>
 5 void foo()
 6 {
 7     std::cout << "foo: " << p << std::endl;
 8 }
 9 
10 namespace
11 {
12     char c = 'c'; // default linkage: external
13     static char d = 'd'; // internal linkage
14 } // unnamed namespace
15 
16 int main()
17 {
18     foo<&c>();
19     foo<&d>(); // error occurs while compiling this line
20 
21     return 0;
22 }

  上面这段代码,在vs 2010环境中,是编译通不过的,其错误是:“foo”: 模板参数“p”:“`anonymous-namespace'::d”: 涉及带有内部链接的对象的表达式不能用作非类型参数。

  同样,在Linux centos 2.6.32-279.el6.x86_64环境中,利用gcc 4.8.1(号称支持C++ 11)也编译出错:‘& {anonymous}::d’不是类型‘const char*’的有效模板实参,因为函数‘{anonymous}::d’没有外部链接。

  所以,关于unnamed namespace内部所含变量或者函数的默认linkage,应该是根据编译器所支持的标准而定的,并不是一味的external或internal。

  

 

  

 

  

  

posted on 2013-10-25 12:24  husoling  阅读(1252)  评论(0编辑  收藏  举报

导航