3

Consider the following class, which can either be in a "non-empty" or "empty" state, and in the "empty" state the other member is default initialized (hence has an indeterminate value):

struct MaybeInt {
  bool has_value;
  int value;

  MaybeInt()      : has_value(false) {}
  MaybeInt(int v) : has_value(true ), value(v) {}
};

Is it allowed to assign from a default-constructed MaybeInt, as in:

MaybeInt empty, another;
another = empty; // OK?

How about construction?

MaybeInt empty, another(empty); // OK?

Does the answer change if MaybeInt::value has type char?

BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • related/dupe: https://stackoverflow.com/questions/4279264/is-reading-an-indeterminate-value-undefined-behavior trying to find a better target – NathanOliver Jul 02 '19 at 18:37
  • @NathanOliver I don't think that is a dupe: the correct answer (by Shafik) relies on the standard text "If an indeterminate value is produced by an evaluation, the behavior is undefined", however it's not immediately clear that `value` is evaluated by the assignment in this question – M.M Jul 02 '19 at 23:24

2 Answers2

2

another = empty is indeed UB because empty.value has an indeterminate value and because the implicitly defined copy constructor of a class copies all members.

The trick is to place the member value in a union. This way the implicitly defined copy constructor of the union copies the object representation :

struct MaybeInt {
  bool has_value;
  union {
    int value;
    char _dumb;
    };

  MaybeInt()      : has_value(false) {}
  MaybeInt(int v) : has_value(true ), value(v) {}
  };

NB: This is a low level trick to have an optional that is trivially copyable. This is realy important for code execution speed. This class can be passed through function call on a cpu register, while it would be impossible if it were not trivially copyable.

Oliv
  • 17,610
  • 1
  • 29
  • 72
  • Clever trick but annoying that it is necessary... I propose that the standard could "specialize" `std::optional` to be trivially-copyable iff `T` is – M.M Jul 02 '19 at 23:30
  • `char _dumb;` is unnecesseary, right? – athos May 18 '23 at 09:25
-1

Your code results in undefined behaviour.

MaybeInt empty, another;
another = empty; 

OK. another has the same undefined value that empty had. Technically working, leading to bugs in practise.

MaybeInt empty, another(empty);

empty contains an undefined value, and this is copied to another which has the same value, only now your class thinks it is defined.

Use std::optional, everything which should be optional is solved in a standard way. (C++ 17)

optional<int> x; // empty or not, you can use `value_or()` to get a default value.
Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78