4

Triggering lvalue-to-rvalue conversion of the inactive member of a union is not a constant expression. That is, given the union:

template<class T, class U>
union A {
  constexpr A(T t) : t_{t} {}
  constexpr A(U u) : u_{u} {}
  T t_;
  U u_;
};

and the constexpr function foo:

template<class T, class U>
constexpr auto foo() {
  A<T, U> a(T{});
  return a.u_;
}

the following program:

int main() {
  constexpr auto test = foo<int, double>();
  return 0;
}

fails with the error message:

error: constexpr variable 'test' must be initialized by a
      constant expression
  constexpr auto test = foo<int, double>();
                 ^      ~~~~~~~~~~~~~~~~~~
note: read of member 'u_' of union with active member 't_' is
      not allowed in a constant expression
  return a.u_;
         ^
note: in call to 'foo()'
  constexpr auto test = foo<int, double>();
                        ^
1 error generated.

Is there a workaround to achieve this behavior within constant expressions in C++14? reinterpret_cast isn't allowed either.

Motivation: I'm trying to make google-test's floating-point comparison utility constexpr.

gnzlbg
  • 7,135
  • 5
  • 53
  • 106

1 Answers1

5

No, there is no way to do type punning in constant expressions in C++14. There's no way to do it outside of constant expressions either; the code you showed will have undefined behavior at runtime.

As an example of why this is disallowed in general, consider this:

union U { int *p; size_t n; };
int a;
constexpr U u = { &a };
static_assert(u.n != 0x400e158, "'a' is at my favourite address");

If this were allowed, an implementation would be required to determine the runtime address of a during compilation.

Richard Smith
  • 13,696
  • 56
  • 78
  • Thank you for your answer Richard. In the Google Test framework there is a class for performing floating point comparison that it is widely used (see: http://stackoverflow.com/a/3423299/1422197). It uses a union to store a floating point number, and switch to its bit representation. Is it also invoking undefined behavior? One could do a `reinterpret_cast` instead, would that be also invoking undefined behavior? – gnzlbg Oct 19 '14 at 09:20
  • I can see in your example why this is disallowed in general within constant expressions. But then in the example one is taking the address of a runtime variable. In the code I showed the objective is to view a `constexpr` variable of some type, as if it were a constexpr variable of a different type (something like reinterpret cast could also do, but it would probably be undefined behavior too). – gnzlbg Oct 19 '14 at 09:26
  • Yes, that code has undefined behavior. Note that GCC has an extension [documented here] (https://gcc.gnu.org/bugs/#nonbugs_c) that allows such type punning through unions in some cases. `reinterpret_cast(my_float)` is ill-formed; that's not a conversion that can be performed by `reinterpret_cast`. – Richard Smith Oct 21 '14 at 00:05
  • I see, i'll try to figure out an alternative way without invoking UB. Thanks a lot for the clarifications! – gnzlbg Oct 21 '14 at 08:41
  • could I memcopy the float (or double) into a sufficiently large unsigned integer type to get rid of the undefined behavior here? – gnzlbg Oct 26 '14 at 13:54
  • Yes, you could, but not within a constant expression (`memcpy` is not `constexpr`). – Richard Smith Nov 04 '14 at 01:12
  • Thanks, i'll keep trying. – gnzlbg Nov 04 '14 at 07:55