C and C++ are infamous for being harder to parse than it's reasonable. C has lots instances of context dependent syntax where code changes meaning depending on what a name is.
You can parse Go without a symbol table, but not C, because stuff like
item *a;
changes completely in meaning depending on the existence of a previous typedef for `item`.
C++ in order to not break compatibility with C has elevated this to insane extremes - there's so much ambiguous syntax in the language.
x = b.get<item>(r);
This expression can either be a function invocation, if `item` is a type and a template method named `get` exists, or else it's a series of comparisons.
item f(r, f);
is also ambiguous: if r and f are types, that's a function, otherwise that's a variable declaration. There's no way to know that unless you have a symbol table, which makes separating syntactical and semantic analysis impossibile at best.
The whole C++ language is full of similar ambiguities, and attempts to fix warts that backfired spectacularly. That's also the reason why writing a C++ frontend is a decade long endeavor that takes lots of people and resources. A full time team of 3 people can probably write a C parser in a few weeks in comparison, and C is big mess too, albeit a smaller mess though.