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

时间:2009-07-13 17:11:31

标签: c++ templates inheritance scope name-lookup

这个问题是对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-&gt;限定变量来解决范围问题。语法。

3 个答案:

答案 0 :(得分:59)

您可以使用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

答案 1 :(得分:22)

这里基类不是非依赖的基类(这意味着基类具有可以在不知道模板参数的情况下确定的完整类型),_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
}

答案 2 :(得分: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();
}