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

Derived template-class access to base-class member-data

提问人:Shamster 提问时间:7/14/2009 最后编辑:CommunityShamster 更新时间:4/21/2017 访问量:58823

问:

这个问题是本线程中提出的问题的进一步。

使用以下类定义:

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!
}

在访问模板类的基类的成员时,似乎我必须始终使用模板样式语法显式限定成员。有没有办法避免这种情况?“using”语句/指令是否可以在模板类方法中发挥作用以简化代码?Bar<T>::_foo_arg

编辑:

通过使用 this-> 语法限定变量,可以解决作用域问题。

C++ 模板 继承 范围 名称查找

评论


答:

0赞 Daniel Earwicker 7/14/2009 #1

在Visual C++ 2008中似乎工作正常。我为您提到的类型添加了一些虚拟定义,但没有给出任何来源。其余的和你说的完全一样。然后强制实例化和调用一个 main 函数。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();
}

评论

0赞 Shamster 7/14/2009
G++ 在上面给出了很多关于定义的错误。但是,由于 BarFunc() 定义中第一次调用 _foo_arg,范围问题仍然存在:“错误:'_foo_arg'未在此范围内声明”。
0赞 Daniel Earwicker 7/14/2009
你的意思是我的虚拟类型声明在 gcc 上给你带来错误吗?
0赞 Shamster 7/14/2009
是的,虚拟类型位于顶部,但示波器错误仍然存在。
0赞 Daniel Earwicker 7/14/2009
我相信 g++ 可能是正确的:您的原始问题。IBM 编译器也大惊小怪,IIRC。
0赞 Shamster 7/14/2009
对不起,我添加了一些测试代码 - 这给了我错误。如果在 BarFunc() 中使用 this->_foo_arg而不是 _foo_arg,则您发布的代码将进行编译。
97赞 sth 7/14/2009 #2

您可以使用来明确表示您指的是类的成员: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;
}

这使编译器清楚地知道成员名称依赖于模板参数,以便它在正确的位置搜索该名称的定义。有关详细信息,另请参阅 C++ 常见问题解答精简版中的此条目

评论

3赞 xtofl 7/14/2009
FAQ 的链接非常有用:它还显示了此问题可能在哪些方面不明显地导致不良行为。
3赞 Catskul 10/1/2010
知道为什么这是真的吗?(FAQ没有完全回答这个问题)
36赞 songyuanyao 6/23/2014 #3

这里的基类不是非依赖基类(这意味着具有完整类型的基类,可以在不知道模板参数的情况下确定),而是一个非依赖名称。标准 C++ 表示不会在依赖基类中查找非依赖名称。_foo_arg

若要更正代码,只需使名称依赖就足够了,因为只有在实例化时才能查找依赖名称,并且此时必须探索的确切基本专用化将被告知。例如:_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
}