In the beginning, there was C. In C, declarations like these are entirely possible (and indeed frequent):
#include <time.h> // defines struct tm { ... }
struct tm tm;
int stat(const char *pathname, struct stat *statbuf); // defined somewhere in POSIX headers
This code is totally normal in C because tags like tm
or stat
do not designate types. Only struct tm
and struct stat
do.
#include <time.h>
tm my_time; // doesn't work in C
Enter C++. In C++, if you define struct tm { ... };
then tm
alone is a type name.
#include <time.h>
tm my_time; // OK in C++
But without the "one exception" detailed in your quote, C code like above would not compile with a C++ compiler.
#include <time.h>
struct tm tm; // would not compile without the exception
// because tm alone already refers to a type
// defined in this scope
Since breaking perfectly good C code is not an intention of C++, the exception was invented and put in place. It basically says that you are allowed to define variables, functions, ans some other stuff with the same name as a class/struct/union tag. If you do, then the tag alone stops being a type name in this scope.
#include <time.h>
struct tm tm; // compiles because of the exception
tm my_time; // no longer compiles because `tm` variable hides the type
struct tm my_time; // OK
So this is the "type/non-type hiding" (because a type is hidden by a non-type)" hack. It's called a hack because it is a slight bend in an otherwise perfectly smooth and boring rule ("every name refers to one thing and one thing only"), which allows something (compatibility with old C code) that would not be possible without. Normal scope-based name hiding is not a hack. It is a perfectly regular thing, not a clever bend in anything.