7

Is the following code legal (in c++11/14)?

bool foo() {
  union bar { int i; bool b; };
  union baz { char c; bar b; };
  auto b = baz{'x'};
  auto barptr = &b.b;
  auto boolptr = &barptr->b;
  new (boolptr) bool{true};
  return b.b.b;
}

This example is silly, but I'm playing around with a variadic variant implementation that uses nested unions instead of a char [] block for the variant members, and allowing this will make my current attempt at the copy constructor cleaner.

To break it down into two subquestions:

  1. Is the assignment of boolptr by accessing a member of barptr legal even though b.b is inactive?
  2. Does the in-place construction of boolptr activate b.b and b.b.b?

References to the standard would be appreciated.

Shea Levy
  • 5,237
  • 3
  • 31
  • 42
  • maybe partially answered here: http://stackoverflow.com/questions/11373203/accessing-inactive-union-member-undefined – wonko realtime Oct 30 '14 at 16:27
  • Is there any specific reason to assume that the expression `b.b` is more legal than `*(&(b.b))` ? Because otherwise it's fairly obvious that both are equally legal (Nobody is seriously going to argue that `b.b` is _less_ legal!) – MSalters Oct 30 '14 at 16:55

1 Answers1

2

As with so many questions about unions and type-punning, it's unclear if your program has defined behavior although I strongly expect it to behave as expected in any sane implementation. What I can say with certitude is that this program:

#include <memory>
#include <new>

template <typename T>
inline void destruct(T& t) { t.~T(); }

template <typename T, typename...Args>
inline void construct(T& t, Args&&...args) {
  ::new((void*)std::addressof(t)) T(std::forward<Args>(args)...);
}

template <typename T>
inline void default_construct(T& t) {
  ::new((void*)std::addressof(t)) T;
}

bool foo() {
  union bar { int i; bool b; };
  union baz { char c; bar b; };
  auto b = baz{'x'};
  destruct(b.c);
  default_construct(b.b);
  construct(b.b.b, true);
  return b.b.b;
}

is standard-compliant, has exactly the effects you desire, and compiles to exactly the same assembly as the original program in modern compilers. Original:

foo():
    movl    $1, %eax
    ret

Guaranteed-compliant:

foo():
    movl    $1, %eax
    ret
Casey
  • 41,449
  • 7
  • 95
  • 125
  • Interesting, but pretty useless. There is no shortage of programs that get optimized away completely, leaving only `return 1;` – Ben Voigt Oct 31 '14 at 18:20
  • How did `Args` template argument to `default_construct` get deduced? It's not used in a deducible context. – Ben Voigt Oct 31 '14 at 18:24
  • @BenVoigt Clang doesn't complain about `Args` either, could both clang and gcc have the same bug? – Casey Oct 31 '14 at 20:16
  • Maybe it's being explicitly provided, as an empty list? In any case, it is better without the unneeded parameter pack. – Ben Voigt Oct 31 '14 at 21:32
  • I've upvoted this as it surely does work, but it doesn't really answer my original question so I'm going to leave it unaccepted. – Shea Levy Nov 01 '14 at 01:39