4

Consider this code:

#define private public

struct Test {
    private:
    int s{9};
};

int main() { }

I've already gone through this and this. I haven't included single header here. Does it mean that behavior of the program is well defined or is it still defined according to C++ standard?

Destructor
  • 14,123
  • 11
  • 61
  • 126
  • 6
    Sounds like good, easily understandable & maintainable code ;-) – Mawg says reinstate Monica Nov 09 '19 at 08:54
  • 2
    See https://stackoverflow.com/a/9110053/1032073 — if you are using C++11 (probably also applies to later standards), then it is not allowed even if you don't include anything. – celtschk Nov 09 '19 at 09:08
  • 1
    Following the link [this](https://stackoverflow.com/questions/9109377/is-it-legal-to-redefine-a-c-keyword), I found [GotW #76](http://www.gotw.ca/gotw/076.htm) with subject **Criminal #2: The Pickpocket**. It states _It is illegal to `#define` a reserved word._ Whether this happens before or after any `#include` is irrelevant concerning standard IMHO. That I got this running on [**coliru**](http://coliru.stacked-crooked.com/a/cd3d35543b43c78e) doesn't prove anything. (That's true for a lot of U.B. things.) – Scheff's Cat Nov 09 '19 at 09:10
  • @celtschk That's a nice link (nearly worth an answer or dup close vote) but what I wonder: _shall_. Why not _must_? ;-) – Scheff's Cat Nov 09 '19 at 09:13
  • 1
    @Scheff Because “shall” is “standardese” — there are rules for what exactly words mean in ISO standards. See [here](https://www.iso.org/foreword-supplementary-information.html) for details. – celtschk Nov 09 '19 at 10:06
  • The result of using the preprocessor to redefine language keywords is undefined behaviour, according to the standard. That is true whether any headers are included or not. – Peter Nov 09 '19 at 11:43
  • 1
    @celtschk: I'm not sure whether the ISO standardized those terms before or after the publication of C89, but the C Standard's use of the term "shall" has never fit that model. The C++ Standard is a bit less loosey-goosey, but I don't think it's 100% consistent either. – supercat Nov 11 '19 at 17:31

1 Answers1

1

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.)

Damon
  • 67,688
  • 20
  • 135
  • 185
  • Re: "the standard library uses names like __data or __begin all the time" -- and that's **exactly** why those names are reserved -- it gives standard library implementors a set of names that are protected from the ravages of users. – Pete Becker Nov 09 '19 at 14:23
  • @PeteBecker: Well yes, obviously. Only just, whether for example `std::vector` has a member called `__data` or not, or whether an algorithm uses an identifier like `__begin` within a brace-scope, and whether a class or function of mine maybe _also_ has an identifier with that same name is widely inconsequential. One is not visible to the other, and doesn't interfere. Preprocessor macros are a different story, of course, but these are kinda evilish intrusive anyway (think e.g. `windows.h` which infamously defines `min` and `max` by default). – Damon Nov 09 '19 at 16:37
  • I'm utterly lost about your point. Yes, names obey scopes. Nevertheless, names that contain two consecutive underscores and names that begin with an underscore followed by a capital letter are reserved for use by the implementation. Keep in mind that it could go the other way: the implementation might define a macro that conflicts with the reserved name that you used in your code. YOU KIDS STAY OUT OF MY NAMESPACE! – Pete Becker Nov 09 '19 at 18:18
  • This is wrong (as an explanation). I can for example do `#define F(x) class x { public: int y; };` and that's perfectly fine, though by your argument it shouldn't be (note that you don't distinguish between defining macros and using in replacement lists here). Now you indeed cannot use keywords as macros, but it has nothing to do with your argument or those sections; it's because it's explicitly prohibited (see `[macro.names]` p2). – H Walters Nov 09 '19 at 19:29