Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legal to activate nested unions via the address of their members?

Tags:

c++

c++11

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.

like image 947
Shea Levy Avatar asked Oct 30 '14 15:10

Shea Levy


People also ask

Can unions be nested?

union { member_list }; Its members can be accessed directly in the scope where this union is declared, without using the x.y or p->y syntax. Anonymous unions can be global, nested, or unnested.

What are the restrictions that must be observed when using C++ unions?

A union cannot have base classes and cannot be used as a base class. A union cannot have non-static data members of reference types. Unions cannot contain a non-static data member with a non-trivial special member function (copy constructor, copy-assignment operator, or destructor).

Is it possible to declare a structure within a union?

You can declare a structure or union type separately from the definition of variables of that type, as described in Structure and union type definition and Structure and union variable declarations; or you can define a structure or union data type and all variables that have that type in one statement, as described in ...

How can you access the member of the union?

Access members of a union We use the . operator to access members of a union. And to access pointer variables, we use the -> operator.


1 Answers

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
like image 50
Casey Avatar answered Oct 06 '22 00:10

Casey