diff --git a/CppCoreGuidelines.md b/CppCoreGuidelines.md index 9539d7d..29d5939 100644 --- a/CppCoreGuidelines.md +++ b/CppCoreGuidelines.md @@ -1,6 +1,6 @@ # C++ Core Guidelines -August 26, 2016 +August 27, 2016 Editors: @@ -7244,28 +7244,44 @@ The compiler catches the attempt to overload a lambda. ## C.union: Unions -??? +A `union` is a `struct` where all members start at the same address so that it can hold only one member at a time. +A `union` does not keep track of which member is stored so the programmer has to get it right; +this is inherently error-prone, but there are ways to compensate. + +A type that is a `union` plus an indicator of which member is currently held is called a *tagged union*, a *discriminated union*, or a *variant*. Union rule summary: -* [C.180: Use `union`s to ???](#Ru-union) +* [C.180: Use `union`s to save Memory](#Ru-union) * [C.181: Avoid "naked" `union`s](#Ru-naked) * [C.182: Use anonymous `union`s to implement tagged unions](#Ru-anonymous) +* [C183: Don't use a `union` for type punning](#Ru-pun) * ??? -### C.180: Use `union`s to ??? - -??? When should unions be used, if at all? What's a good future-proof way to re-interpret object representations of PODs? -??? variant +### C.180: Use `union`s to save memory ##### Reason - ??? +A `union` allows a single piece of memory to be used for different types of objects at different times. +Consequently, it can be used to save memory when we have several objects that are never used at the same time. ##### Example + union Value { + int x; + double d; + }; - ??? + Value v = { 123 }; // now v holds an int + cout << v.x << '\n'; // write 123 + v.d = 987.654; // now v holds a double + cout << v.d << '\n'; // write 987.654 + +But heed the warning: [Avoid "naked" `union`s](#Ru-naked) + +##### Example + + ??? short-string optimization; safe union without dscriminant ??? ##### Enforcement @@ -7275,18 +7291,44 @@ Union rule summary: ##### Reason - +A *naked union* is a union without an associated indicator which member (if any) it holds, +so that the programmer has to keep track. Naked unions are a source of type errors. -**Alternative**: Wrap them in a class together with a type field. +###### Example, bad -**Alternative**: Use `variant`. + union Value { + int x; + double d; + }; -##### Example + Value v; + v.d = 987.654; // v holds a double - ??? +So far, so good, but we can easily misuse the `union`: + cout << v.x << '\n'; // BAD, undefined behavior: v holds a double, but we read it as an int +Note that the type error happened without any explicit cast. +When we tested that program the last value printed was `1683627180` which it the integer value for the bit pattern for `987.654`. +What we have here is an "invisible" type error that happens to give a result that could easily look innocent. + +And, talking about "invisible", this code produced no output: + + v.x = 123; + cout << v.d << '\n'; // BAD: undefined behavior + +###### Alternative + +Wrap a `union` in a class together with a type field. + +The soon-to-be-standard `variant` type (to be found in ``) does that for you: + + variant v; + v = 123; // v holds an int + int x = get(v); + v = 123.456; // v holds a double + w = get(v); ##### Enforcement @@ -7296,16 +7338,149 @@ Naked unions are a source of type errors. ##### Reason -??? +A well-designed tagged usin is type safe. +An *anonymous* union simplifies the definition of a class with a (tag,union) pair. ##### Example - ??? +This example is mostly borrowed from TC++PL4 pp216-218. +You can look there for an explanation. + +The code is somewhat elaborate. +Handling a type with user-defined assignment and destructor is tricky. +Saving programmers from haing to write such code is one reason for including `variant` in the standard. + + class Value { // two alternative representations represented as a union + private: + enum class Tag { number, text }; + Tag type; // discriminant + + union { // representation (note: anonymous union) + int i; + string s; // string has default constructor, copy operations, and destructor + }; + public: + struct Bad_entry { }; // used for exceptions + + ~Value(); + Value& operator=(const Value&); // necessary because of the string variant + Value(const Value&); + // ... + int number() const; + string text() const; + + void set_number(int n); + void set_text(const string&); + // ... + }; + + int Value::number() const + { + if (type!=Tag::number) throw Bad_entry{}; + return i; + } + + string Value::text() const + { + if (type!=Tag::text) throw Bad_entry{}; + return s; + } + + void Value::set_number(int n) + { + if (type==Tag::text) { + s.~string(); // explicitly destroy string + type = Tag::number; + } + i = n; + } + + void Value::set_text(const string& ss) + { + if (type==Tag::text) + s = ss; + else { + new(&s) string{ss}; // placement new: explicitly construct string + type = Tag::text; + } + } + + Value& Value::operator=(const Value& e) // necessary because of the string variant + { + if (type==Tag::text && e.type==Tag::text) { + s = e.s; // usual string assignment + return *this; + } + + if (type==Tag::text) s.~string(); // explicit destroy + + switch (e.type) { + case Tag::number: + i = e.i; + break; + case Tag::text: + new(&s)(e.s); // placement new: explicit construct + type = e.type; + } + + return *this; + } + + Value::~Value() + { + if (type==Tag::text) s.~string(); // explicit destroy + } ##### Enforcement ??? +### C.183: Don't use a `union` for type punning + +##### Reason + +It is undefined behavior to read a `union` member with a different type from the one with which it was written. +Such punning is invisible, or at least harder to spot than using a named cast. +Type punning using a `union` is a source of errors. + +##### Example, bad + + union Pun { + int x; + unsigned char c[sizeof(int)]; + }; + +The idea of `Pun` is to be able to look at the characte representation of an `int`. + + void bad(Pun& u) + { + u.x = 'x'; + cout << u.c[0] << '\n'; // undefined behavior + } + +If you wanted to see the bytes of an `int`, use a (named) cast: + + void if_you_must_pun(int& x) + { + auto p = reinterpret_cast(&x); + cout << p[0] << '\n'; // undefined behavior + // ... + } + +Accessing the result of an `reinterpret_cast` to a different type from the objects declared type is still undefined behavior, +but at least we can see that something tricky is going on. + +##### Note + +Unfortunately, `union`s are commonly used for type punning. +We don't consider "sometimes, it works as expected" a strong argument. + +##### Enforcement + +??? + + + # Enum: Enumerations Enumerations are used to define sets of integer values and for defining types for such sets of values.