85 Advanced The Hundred
The Member That Wasn't There
the base class declares it, plain as day
#include <iostream>
template <class T> struct Storage {
T value{};
};
template <class T> struct Wrapper : Storage<T> {
void set(T v) { value = v; }
T get() { return value; }
};
int main() {
Wrapper<int> w;
w.set(42);
std::cout << w.get() << "\n";
}
Run it. What does it print?
Answer
Nothing — it doesn't even compile: error: 'value' was not declared in this scope.
The member the whole class is built on is, apparently, not there.
Why¶
Storage<T> is a dependent base — which class it actually is depends on T, and
someone could later specialize it: a Storage<bool> with no value at all is fair game.
So the compiler refuses to assume: while parsing Wrapper, unqualified lookup simply does
not search dependent bases, finds no value anywhere, and rejects the code before
Wrapper<int> is ever instantiated. Writing this->value changes everything — this
makes the expression dependent, so lookup is deferred to instantiation time, when
Storage<int> is a real class with a real value. With a non-template base, none of
this applies: unqualified value just works.
The fix¶
Any of these three, each saying "trust me, it's in the base":
void set(T v) { this->value = v; } // defer the lookup to instantiation
T get() { return Storage<T>::value; } // or name the base explicitly
using Storage<T>::value; // or import it once, in the class body
Takeaway: inside a template, members of a dependent base are invisible to unqualified
lookup — reach for them with this->.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo — prints 42 (the shipped main.cpp
uses the fix); add -DSHOW_BUG to get the error above.