Derived template-class access to base-class member-data
Asked Answered
B

3

91

This question is a furtherance of the one asked in this thread.

Using the following class definitions:

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

When accessing the members of the template-class's base-class, it seems like I must always explicitly qualify the members using the template-style syntax of Bar<T>::_foo_arg. Is there a way to avoid this? Can a 'using' statement/directive come into play in a template class method to simplify the code?

Edit:

The scope issue is resolved by qualifying the variable with this-> syntax.

Bollay answered 13/7, 2009 at 17:11 Comment(0)
C
100

You can use this-> to make clear that you are referring to a member of the class:

void Bar<T>::BarFunc () {
    std::cout << this->_foo_arg << std::endl;
}

Alternatively you can also use "using" in the method:

void Bar<T>::BarFunc () {
    using Bar<T>::_foo_arg;             // Might not work in g++, IIRC
    std::cout << _foo_arg << std::endl;
}

This makes it clear to the compiler that the member name depends on the template parameters so that it searches for the definition of that name in the right places. For more information also see this entry in the C++ Faq Lite.

Cryptoclastic answered 13/7, 2009 at 17:50 Comment(2)
The link to the Faq is very useful: it also shows where this issue can unvisibly cause unwanted behaviour.Nato
Any idea why this is true? (the FAQ does not answer this completely)Arbitress
C
37

Here the base class is not a nondependent base class( which means one with a complete type that can be determined without knowing the template arguments), and _foo_arg is a nondependent name. Standard C++ says that nondependent names are not looked up in dependent base classes.

To correct the code, it suffices to make the name _foo_arg dependent because dependent names can be looked up only at the time of instantiation, and at that time the exact base specialization that must be explored will be known. For example:

// solution#1
std::cout << this->_foo_arg << std::endl;

An alternative consists in introducing a dependency using a qualified name:

// solution#2
std::cout << Foo<T>::_foo_arg << std::endl;

Care must be taken with this solution, because if the unqualified nondependent name is used to form a virtual function call, then the qualification inhibits the virtual call mechanism and the meaning of the program changes.

And you can bring a name from a dependent base class in the derived class once by 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
}
Cassirer answered 23/6, 2014 at 14:33 Comment(0)
T
0

Appears to work fine in Visual C++ 2008. I've added some dummy definitions for the types you mentioned but gave no source for. The rest is exactly as you put it. Then a main function to force BarFunc to be instantiated and called.

#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();
}
Trudi answered 13/7, 2009 at 17:31 Comment(5)
g++ hands out a lot of errors regarding the definitions up top. However, the scope problem still remains with: "error: ‘_foo_arg’ was not declared in this scope," due to the first call to _foo_arg in the BarFunc() definition.Bollay
Do you mean my dummy type declarations give you errors on gcc?Trudi
yes, the dummy types at top, but the scope error remains, as well.Bollay
I believe g++ may be correct re: your original issue. The IBM compiler kicked up the same fuss, IIRC.Trudi
Sorry, I had added some test code - that was giving me the error. The code as you posted compiles if using the this->_foo_arg instead of _foo_arg in BarFunc().Bollay

© 2022 - 2024 — McMap. All rights reserved.