Contrasting solutions for visibility when inheriting from base class templates

40 Views Asked by At

In the following code will fail to compile because, despite the chain of public inheritance, HasFlag is not visible in DerivedFoo.

class BasicFoo {
    public:  bool HasFlag() const { return m_flag; }
    private: bool m_flag = false;
};

template <typename T>
class Foo : public BasicFoo {
    public:  bool Test() const { return HasFlag(); }
};

template <typename T>
class DerivedFoo : public Foo<T> {
    public:
        // HasFlag is not visible here.
        bool Test2() const { return HasFlag(); }
};

The why of this is covered in other questions. My question is whether there is any difference between these two solutions:

        // Option 1:  Explicitly pull it into scope from immediate base.
        using Foo<T>::HasFlag;
        bool Test2() const { return HasFlag(); }
        // Option 2:  Explicitly pull it from the root class.
        using BasicFoo::HasFlag;
        bool Test2() const { return HasFlag(); }

Is pulling the method from the root class, which is not a class template, somehow more efficient? There is only ever one BasicFoo::HasFlag. If DerivedFoo<T> is instantiated for many Ts, there are, in effect, many identical Foo<T>::HasFlags. Does that matter?

1

There are 1 best solutions below

1
Jan Schultke On

Semantically the two solutions are equivalent. They are both using-declarations which make the HasFlag member available in the derived class.

using BasicFoo::HasFlag;

This might be better though, because it is not dependent on T, and the compiler can diagnose whether the HasFlag member exists in BasicFoo before instantiating the DerivedFoo class template. Intuitively, this should compile slightly faster, although it's unlikely to make any noticeable difference. When in doubt, benchmark your compilation (e.g. with LLVM's -ftime-trace).

If DerivedFoo<T> is instantiated for many Ts, there are, in effect, many identical Foo<T>::HasFlags.

This isn't true; there is only one HasFlag member in BasicFoo. The difference between the two solutions is only the name lookup, not the amount of members which is created.

Alternative Solutions

bool Test2() const {
    // access to this makes HasFlag() dependent on T, so this won't fail to compile
    return this->HasFlag();
}

bool Test2() const {
    // qualified lookup also forces a delayed check, making this compile
    return BasicFoo::HasFlag();
}