对基类成员数据的派生模板类访问

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&lt;T&gt;::_foo_arg 的模板样式语法明确限定成员。有没有办法避免这种情况? 'using' 语句/指令能否在模板类方法中发挥作用以简化代码?

编辑:

通过使用 this-> 语法限定变量来解决范围问题。

【问题讨论】:

【参考方案1】:

您可以使用this-&gt; 来表明您指的是班级成员:

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,则您发布的代码将编译。

以上是关于对基类成员数据的派生模板类访问的主要内容,如果未能解决你的问题,请参考以下文章

对基类成员数据的派生模板类访问

C++ 派生模板类:访问实例的受保护成员

继承对基类私有数据成员的访问(在派生类中继承基类成员函数)

继承和派生

回顾C++:访问权限控制之 公有保护私有继承用法详解与总结

派生类模板看不到基类的成员[重复]