The turbofish rule is much easier to learn: just use ::<> when explicitly providing types for a call. The C++ concept of a "dependent qualified name" is a lot harder to explain.
What's important isn't how often you need to help the compiler: it's how easy the rules are. The turbofish is unfortunate, but it's nowhere near as bad as typename.
I don't think that's true. Consider a modified version of [1]:
template<typename T> class X {
void foo() {
typename T::A* pa;
}
}
The problem here is that C++ can't parse this without knowing whether T::A is a type or not. Otherwise it might be "T::A multiplied by pa". This is the lexer hack in action.
Rust, by contrast, has no such limitation [2]:
trait SomeTrait {
type A;
}
struct X<T> {
f: T,
}
impl<T> X<T> where T: SomeTrait {
fn foo() {
let pa: *mut T::A;
}
}
This compiles and runs just fine with no need for a turbofish on T::A, because Rust has no lexer hack.
What's important isn't how often you need to help the compiler: it's how easy the rules are. The turbofish is unfortunate, but it's nowhere near as bad as typename.