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


71

这个问题是一个在问的赞助这个线程

使用以下类定义:

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。有办法避免这种情况吗?可以在模板类方法中使用“使用”语句/指令来简化代码吗?

编辑:

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

Answers:


78

您可以用来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 ++ Faq Lite中的此项


3
常见问题解答的链接非常有用:它还显示了此问题在哪些地方会明显导致不必要的行为。
xtofl

3
知道为什么这是真的吗?(FAQ不能完全回答问题)
Catskul

29

在这里,基类不是非依赖性基类(这是一种具有完全类型的类型,可以在不知道模板参数的情况下进行确定),并且_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
}

1

在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 ++发出了很多有关定义的错误。但是,由于在BarFunc()定义中首次调用_foo_arg,范围问题仍然存在:“错误:在此范围中未声明'_foo_arg'”。
Shamster

您是说我的虚拟类型声明给您gcc错误吗?
Daniel Earwicker,2009年

是的,虚拟类型位于顶部,但范围错误也仍然存在。
Shamster

我相信g ++可能对您的原始问题是正确的。IBM编译器也引起了同样的麻烦,即IIRC。
Daniel Earwicker,2009年

抱歉,我添加了一些测试代码-这给了我错误。如果在BarFunc()中使用this-> _ foo_arg而不是_foo_arg,则会编译您发布的代码。
Shamster
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.