名字查找,模板,和基类成员访问

 

原文来自 http://gcc.gnu.org/onlinedocs/gcc/Name-lookup.html

 

    C++标准规定,当解析一个模板函数或模板类时,所有不依赖于模板形参的名字被绑定到它们当前的定义。只有依赖于模板形参的名字在实例化点被查找。考虑下面的例子:

       void foo(double);
     
       struct A {
         template <typename T>
         void f () {
           foo (1);        // 1
           int i = N;      // 2
           T t;
           t.bar();        // 3
           foo (t);        // 4
         }
     
         static const int N;
       };

    在这里,名字 foo 和 N 出现在不依赖于类型 T 的上下文中。因此,编译器需要它们在模板使用的上下文中被定义,不是在实例化点之前,并且在这里将分别使用 ::foo(double) 和 A::N。特别地,当将它传递给 ::foo(double) 时,它会被从 int 转换到 double。

 

    相反地,bar 和在标记 4 中对 foo 的调用是在依赖于类型 T 的上下文中被使用的,因此,它们只在实例化时查找,并且你可以在声明模板后为它们提供声明,但是要在实例化它之前。特别地,如果你实例化了 A::f<int>,如果提供了 ::foo(int) 的重载,那么最后一行会调用这个重载的 ::foo(int),甚至在结构 A 的声明之后提供这个函数。

 

    在查找 dependent 和 non-dependent 名字的这种区别叫做 two-stage(或者 dependent)名字查找。自从 3.4 版以来,G++实现了它。

 

    一些时候 two-stage 名字查找会导致与 non-template 不同的行为。最普遍的例子可能是这个:

       template <typename T> struct Base {
         int i;
       };
     
       template <typename T> struct Derived : public Base<T> {
         int get_i() { return i; }
       };

    在 get_i() 中,i 没有在一个 dependent 上下文中被使用,因此编译器会在外围名字空间作用域中查找(这里是全局作用域)。它不会在基类中查找,因为它是 dependent 并且你甚至可以在声明 Derived 之后声明 Base 的特化,因此,编译器不能真正地知道 i 会引用什么。如果没有全局变量 i,那么你会得到一个错误。

 

    为了使你想要的基类成员明确,你需要推迟查找直到实例化时,在这时基类被确定。为了实现这个目的,你需要在 dependent 上下文中来访问 i,你可以使用 this->i(记住 this 是 Derived<T>* 类型,因此明显是 dependent)或使用 Base<T>::i。此外,Base<T>::i 可以被 using-declaration 放入作用域。

  

    另外一个相似的调用基类成员函数的例子: 

       template <typename T> struct Base {
           int f();
       };
     
       template <typename T> struct Derived : Base<T> {
           int g() { return f(); };
       };

     同样地,对 f() 的调用时不依赖于模板参数的(没有依赖于类型 T 的参数,并且没有明确指定这个调用需要在一个 dependent 上下文中)。因此一个这样一个函数的全局声明必须可用,因为直到实例化时,在基类中的 f() 都是不可视的。编译器必然会产生下面这个错误消息:

       x.cc: In member function `int Derived::g()':
       x.cc:6: error: there are no arguments to `f' that depend on a template
          parameter, so a declaration of `f' must be available
       x.cc:6: error: (if you use `-fpermissive', G++ will accept your code, but
          allowing the use of an undeclared name is deprecated)

    为了使代码有效,可以使用 this->f() 或 Base<T>::f()。使用 -fpermissive 也可以让编译器接受这个代码,通过标记所有在模板定义时没有声明可见的函数调用为在实例化时延迟查找,就像它是一个 dependent 调用。我们不建议使用 -fpremissive 去解决不可用的代码,并且它也只会捕捉在基类的函数调用,而不是在基类的变量(就像上面里例子中的一样)。

  

    注:一些编译器(包括在 G++3.4 之前的版本)使得这些例子错误,并且接受以上代码而没有错误。那些编译器没有正确实现 two-stage 名字查找。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值