86 Advanced The Hundred
The Type the Compiler Refused to Believe
spelled right, scoped right, and still not a type
#include <iostream>
#include <vector>
template <class C> void scale_front(C& c) {
C::value_type* p = &c.front(); // point at the first element
*p *= 10;
std::cout << "front is now " << c.front() << "\n";
}
int main() {
std::vector<int> v{7, 8, 9};
scale_front(v);
}
Run it. What does it print?
Answer
Nothing — it doesn't compile, and GCC's first complaint isn't about the type at all:
error: 'p' was not declared in this scope.
Why¶
When the compiler first parses a template it knows nothing about C, so it cannot look
inside C:: to see what value_type is — and the standard tells it to assume that a
qualified dependent name is a value. Under that reading the line is not a declaration
at all: it parses as the assignment (C::value_type * p) = &c.front(), a product being
assigned to, built out of a p that nobody declared — and it is that undeclared p the
compiler complains about first. The value assumption is not paranoia: a specialization can
make value_type a static constexpr int, and given a p already in scope the statement
C::value_type* p; really does compile as a product, GCC shrugging only statement has no
effect (-Wunused-value). typename is how you overrule the default, and once
instantiation proves you meant a type, GCC all but writes the fix:
error: dependent-name 'C::value_type' is parsed as a non-type, but instantiation yields a type
note: say 'typename C::value_type' if a type is meant
Since C++20, typename is implied where only a type could possibly appear — a member
declaration, a function's return type — but a function body is a context where declarations
and expressions compete, so that whole category is excluded: inside one, typename stays
mandatory in every standard.
The fix¶
Takeaway: inside a template, a dependent qualified name means a value unless you say
typename.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo — prints front is now 70 (the
shipped main.cpp uses the fix); add -DSHOW_BUG to get the errors above.