对基类成员数据的派生模板类访问
Posted
技术标签:
【中文标题】对基类成员数据的派生模板类访问【英文标题】:Derived template-class access to base-class member-data 【发布时间】:2009-07-13 17:11:31 【问题描述】:这个问题是对this thread 中提出的问题的推进。
使用以下类定义:
template <class T>
class Foo
public:
Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
/* do something for foo */
T Foo_T; // either a TypeA or a TypeB - TBD
foo_arg_t _foo_arg;
;
template <class T>
class Bar : public Foo<T>
public:
Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
: Foo<T>(bar_arg) // base-class initializer
Foo<T>::Foo_T = T(a_arg);
Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
: Foo<T>(bar_arg)
Foo<T>::Foo_T = T(b_arg);
void BarFunc ();
;
template <class T>
void Bar<T>::BarFunc ()
std::cout << _foo_arg << std::endl; // This doesn't work - compiler error is: error: ‘_foo_arg’ was not declared in this scope
std::cout << Bar<T>::_foo_arg << std::endl; // This works!
当访问模板类基类的成员时,似乎我必须始终使用Bar<T>::_foo_arg
的模板样式语法明确限定成员。有没有办法避免这种情况? 'using' 语句/指令能否在模板类方法中发挥作用以简化代码?
编辑:
通过使用 this-> 语法限定变量来解决范围问题。
【问题讨论】:
【参考方案1】:您可以使用this->
来表明您指的是班级成员:
void Bar<T>::BarFunc ()
std::cout << this->_foo_arg << std::endl;
您也可以在方法中使用“using
”:
void Bar<T>::BarFunc ()
using Bar<T>::_foo_arg; // Might not work in g++, IIRC
std::cout << _foo_arg << std::endl;
这使编译器清楚地知道成员名称取决于模板参数,以便它在正确的位置搜索该名称的定义。如需更多信息,另请参阅this entry in the C++ Faq Lite。
【讨论】:
常见问题解答的链接非常很有用:它还显示了此问题可能在哪些地方不可见地导致不良行为。 知道为什么这是真的吗? (FAQ 没有完全回答这个问题)【参考方案2】:这里的基类不是一个非依赖的基类(这意味着一个具有完整类型的基类,可以在不知道模板参数的情况下确定),_foo_arg
是一个非依赖的名称。标准 C++ 说,不依赖的名称不会在依赖的基类中查找。
要更正代码,只需将名称 _foo_arg
设置为依赖即可,因为依赖名称只能在实例化时查找,届时将知道必须探索的确切基础特化。例如:
// solution#1
std::cout << this->_foo_arg << std::endl;
另一种方法是使用限定名称引入依赖项:
// solution#2
std::cout << Foo<T>::_foo_arg << std::endl;
这个解决方案必须小心,因为如果使用非限定的非依赖名称来形成虚函数调用,那么限定会抑制虚调用机制,程序的含义会发生变化。
您可以通过using
从派生类中的依赖基类中带一个名称:
// solution#3
template <class T>
class Bar : public Foo<T>
public:
...
void BarFunc ();
private:
using Foo<T>::_foo_arg;
;
template <class T>
void Bar<T>::BarFunc ()
std::cout << _foo_arg << std::endl; // works
【讨论】:
【参考方案3】:在 Visual C++ 2008 中似乎可以正常工作。我为您提到的类型添加了一些虚拟定义,但没有提供任何来源。其余的和你说的完全一样。然后是强制BarFunc
实例化和调用的主函数。
#include <iostream>
class streamable ;
std::ostream &operator<<(std::ostream &os, streamable &s) return os;
class foo_arg_t : public streamable ;
class a_arg_t : public streamable ;
class b_arg_t : public streamable ;
template <class T>
class Foo
public:
Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
/* do something for foo */
T Foo_T; // either a TypeA or a TypeB - TBD
foo_arg_t _foo_arg;
;
template <class T>
class Bar : public Foo<T>
public:
Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
: Foo<T>(bar_arg) // base-class initializer
Foo<T>::Foo_T = T(a_arg);
Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
: Foo<T>(bar_arg)
Foo<T>::Foo_T = T(b_arg);
void BarFunc ();
;
template <class T>
void Bar<T>::BarFunc ()
std::cout << _foo_arg << std::endl;
std::cout << Bar<T>::_foo_arg << std::endl;
int main()
Bar<a_arg_t> *b = new Bar<a_arg_t>(foo_arg_t(), a_arg_t());
b->BarFunc();
【讨论】:
g++ 给出了很多关于顶部定义的错误。但是,范围问题仍然存在:“错误:'_foo_arg' 未在此范围内声明”,因为在 BarFunc() 定义中第一次调用 _foo_arg。 你的意思是我的虚拟类型声明给你在 gcc 上的错误吗? 是的,虚拟类型在顶部,但范围错误仍然存在。 我相信 g++ 可能是正确的:你原来的问题。 IBM 编译器引发了同样的大惊小怪,IIRC。 抱歉,我添加了一些测试代码 - 这给了我错误。如果在 BarFunc() 中使用 this->_foo_arg 而不是 _foo_arg,则您发布的代码将编译。以上是关于对基类成员数据的派生模板类访问的主要内容,如果未能解决你的问题,请参考以下文章