大龙的博客

常用链接

统计

最新评论

模板类的子类访问不到父类的成员变量问题

很少用模板类,今天用googlemock给一个api打桩,那个api的函数居然是nonvirutal的,gmock帮助手册说Mocking Nonvirtual Methods就得用模板类,所以改了源码,加了模板。但是编译时遇到错误,原来都可以在子类成员函数中访问到的父类的成员变量,现在提示没有定义过。

比如

template<typename T>

class A

{

public: 

   virtual int foo(){return 0};

protected:

   int m_a;

};

template<typename T>

class B : public A<T>

{

public:

   virtual int foo() {return m_a;}

};

这样用gcc就会报错,B类的foo函数中m_a找不到。

解决办法是 B类的foo函数这么写

 virtual int foo() {return this->m_a;}

或者

 virtual int foo() {return A::m_a;}

原因请看这段话:

//10.8.2 Name lookup, templates, and accessing members of base classes
//The C++ standard prescribes that all names that are not dependent on template parameters
//are bound to their present definitions when parsing a template function or class.1 Only
//names that are dependent are looked up at the point of instantiation. For example, consider
//
//       void foo(double);
//
//       struct A {
//         template 
//         void f () {
//           foo (1);        // 1
//           int i = N;      // 2
//           T t;
//           t.bar();        // 3
//           foo (t);        // 4
//         }
//
//         static const int N;
//       };
//
//Here, the names foo and N appear in a context that does not depend on the type of T. The
//compiler will thus require that they are defined in the context of use in the template,
//not only before the point of instantiation, and will here use ::foo(double) and A::N,
//respectively. In particular, it will convert the integer value to a double when passing
// it to ::foo(double).
//
//Conversely, bar and the call to foo in the fourth marked line are used in contexts that
//do depend on the type of T, so they are only looked up at the point of instantiation,
//and you can provide declarations for them after declaring the template, but before
//instantiating it. In particular, if you instantiate A::f, the last line will call an
//overloaded ::foo(int) if one was provided, even if after the declaration of struct A.
//
//This distinction between lookup of dependent and non-dependent names is called two-stage
//(or dependent) name lookup. G++ implements it since version 3.4.
//
//Two-stage name lookup sometimes leads to situations with behavior different from non-template
//codes. The most common is probably this:
//
//       template  struct Base {
//         int i;
//       };
//
//       template  struct Derived : public Base {
//         int get_i() { return i; }
//       };
//
//In get_i(), i is not used in a dependent context, so the compiler will look for a name declared
//at the enclosing namespace scope (which is the global scope here). It will not look into the base
//class, since that is dependent and you may declare specializations of Base even after declaring
//Derived, so the compiler can't really know what i would refer to. If there is no global variable
//i, then you will get an error message.
//
//In order to make it clear that you want the member of the base class, you need to defer lookup
//until instantiation time, at which the base class is known. For this, you need to access i in a
//dependent context, by either using this->i (remember that this is of type Derived*, so is
//obviously dependent), or using Base::i. Alternatively, Base::i might be brought into scope
//by a using-declaration.
//
//Another, similar example involves calling member functions of a base class:
//
//       template  struct Base {
//           int f();
//       };
//
//       template  struct Derived : Base {
//           int g() { return f(); };
//       };
//
//Again, the call to f() is not dependent on template arguments (there are no arguments that depend on
//the type T, and it is also not otherwise specified that the call should be in a dependent context).
//Thus a global declaration of such a function must be available, since the one in the base class is
//not visible until instantiation time. The compiler will consequently produce the following error message:
//
//       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)
//
//To make the code valid either use this->f(), or Base::f(). Using the -fpermissive flag will also
//let the compiler accept the code, by marking all function calls for which no declaration is visible
//at the time of definition of the template for later lookup at instantiation time, as if it were a
//dependent call. We do not recommend using -fpermissive to work around invalid code, and it will also
//only catch cases where functions in base classes are called, not where variables in base classes are
//used (as in the example above).
//
//Note that some compilers (including G++ versions prior to 3.4) get these examples wrong and accept above
//code without an error. Those compilers do not implement two-stage name lookup correctly.
//


原理是名字查找的规则问题。由于还要加班调程序,下次再写名字查找的原理

posted on 2012-03-04 11:09 大龙 阅读(2392) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理