Although it will certainly "work" and you are pretty much guaranteed that no "real" UB is invoked, I think that to the letter of the standard, it may be argued that Undefined Behavior is invoked, according to the lengthy, and somewhat ambiguous elaborations of [lex]
.
Similarly, there is no "real harm" in doing something like #define __included_foo_h
or struct bar { int _Data; };
, but the standard explicitly says in [lex.name]
that these names are reserved. Which, like it or not, is just what it is.
For whatever it's worth, the standard library uses names like __data
or __begin
all the time, so obviously there is no hard technical reason not to do it. Only just... the standard library is a part of the implementation, and your program doesn't have that liberty. So, yeah, it probably doesn't matter at all, but you're still not allowed to do it (if for no other reason than fearing that you might break standard library functionality in a very non-obvious, impossible-to-debug manner).
In particular:
[lex.key]
The identifiers shown in Table 5 are reserved for use as keywords (that is, they are unconditionally treated as keywords in phase 7) except in an attribute-token
[lex.phases]
7.White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. The resulting tokens are syntactically and semantically analyzed and translated as a translation unit.
True, in phase 4, macro substitutions occur, so phase 7 will not "see" the redefined keyword as such. Still, it clearly says "reserved" and "unconditionally". So, if you were allowed to just ignore that based on "but the compiler doesn't see it", then you might as well e.g. #define __inline inline [[gnu:always_inline]]
with the same logic. Who cares if double-underscores are reserved, the compiler doesn't see it!
I don't think that, although it will certainly "work", it is a strictly legitimate point of view.
(Fun fact: I've used #define private public
once myself, worked like a charm.)