skypjack
skypjack

Reputation: 50568

CRTP and visibility of a type that is defined by the base class

Here a brief example of a code that works. It helps to introduce the actual question.
The specifiers for the visibility are the same used in the real code.

class Base {
public:
    using foo = int;
    virtual ~Base() { }

protected:
    static foo bar() noexcept {
        static foo v = 0;
        return v++;
    }
};

template<class Derived>
class Class: public Base {
    static foo get() noexcept {
        static foo v = bar();
        return v;
    }
};

int main() { }

It follows the previous example, even though slightly modified.
A template parameter has been added to the base class and the derived one has been updated accordingly.
This one doesn't compile.

template<typename T>
class Base {
public:
    using foo = int;
    virtual ~Base() { }

protected:
    static foo bar() noexcept {
        static foo v = 0;
        return v++;
    }
};

template<class Derived, typename T>
class Class: public Base<T> {
    static foo get() noexcept {
        static foo v = bar();
        return v;
    }
};

int main() { }

The error is quite clear indeed and the problem is not to solve it:

main.cpp:18:12: error: ‘foo’ does not name a type
     static foo get() noexcept {
            ^
main.cpp:18:12: note: (perhaps ‘typename BaseComponent<T>::foo’ was intended)

The actual question is: why foo is not visible without a scope specifier in the second example?
I mean, Class is derived from Base<T>, that is (at least in my mind) a fully defined type, thus foo should be part of the base class and thus visible to the derived one, as it happens in the first example.

It follows an example that, according to what I guess of the problem, actually compiles and should not, or at least should behave like the previous one:

template <typename T>
struct B {
    using foo = T;
};

struct D: public B<int> {
    static foo bar;
};

int main() {
    B<int> *b = new D;
}

Could it be due to the fact that the derived class is not a templated one in this case?
Honestly, it seems to me a bit strange, because the foo type is part of the base class that is still a templated one, so it shouldn't be much different from the previous one.

It goes without saying that I'm wrong, but I cannot figure out what's wrong in my thoughts.

Thank you in advance for the help.

Upvotes: 2

Views: 395

Answers (1)

vsoftco
vsoftco

Reputation: 56577

That's because of name lookup rules. If your base class is a template, the unqualified name in base is not resolved. The reason is that later in your code you may have a template specialization of that base which doesn't define the name, or for which the name means something completely different. It's too complicated for the compiler to figure out whether you have a specialization, and if so, whether your name means the same thing in it. So it prefers to defer the name lookup. To make the compiler "believe you", then you need to either use a qualified name, or this->name instead (for members, not for typedefs), and the compiler will resolve the name.

Upvotes: 3

Related Questions