They have to draw the line between "inner" and "outer" scope somewhere. As you mentioned, it's easier to find the end of the production when the identifiers within it can be resolved. It's easier to implement in the compiler, and it's easier to consistently specify in the language standard. That is probably why the first compilers did it that way, and why the language always requires the typename
and template
keywords to resolve syntactic ambiguities caused by undefined (nested) identifiers in template expansion.
And an identifier can never be used before it appears in a declaration, which is a wonderful rule to help human or machine readers to find the relevant declaration. (Except between class members, where special two-pass parsing is applied to improve convenience.)
Once it's done one way, a commitment is made and the language can't change. Except by adding a feature, that is, and that's how the problem was solved by C++11.
template<class Lhs, class Rhs>
auto adding_func(const Lhs &lhs, const Rhs &rhs) -> decltype(lhs+rhs)
{return lhs + rhs;}
By the way, there's another workaround which they don't mention:
template<class Lhs, class Rhs>
decltype( declval< Lhs >() + declval< Rhs >() )
adding_func(const Lhs &lhs, const Rhs &rhs) {return lhs + rhs;}