Given struct B
, which inherits an anonymous union
data member from struct A
:
#include <cstddef>
struct A
{
union
{
struct { int a, b, c; };
int vals[3];
};
};
struct B: A
{
constexpr B(int x)
:
A{{{ x, x, x }}}
{}
constexpr int& operator[](size_t i)
{
return this->vals[i];
}
constexpr const int& operator[](size_t i) const
{
return this->vals[i];
}
};
I declare a constexpr
variable of type B
, then calling its operator[]
to assign the return value to a constexpr int
:
int main()
{
constexpr B b(7);
constexpr int i = b[2];
return 0;
}
However Clang 3.8 gives me the error message
constexpr variable 'i' must be initialized by a constant expression
The issue is related to the anonymous union
, since when I simply use int vals[3]
then everything works fine.
Is there a C++14 constexpr
restriction I'm missing?
This is not allowed:
constexpr int i = b[2];
b[2]
is not a constant expression because it contains (ref: N4140 [expr.const]/2.8)
an lvalue-to-rvalue conversion (4.1) or modification (5.17, 5.2.6, 5.3.2) that is applied to a glvalue that refers to a non-active member of a union or a subobject thereof;
The active member of the union is the struct because you initialized that member. The int array is inactive.
If you changed the operator[]
function to switch
and return a member of the struct instead, it should work.
Note: accessing the inactive member causes undefined behaviour. Although common compilers support union aliasing as an extension, it would avoid some trouble if you could design your code to not use union aliasing.
There are issues with the anonymous struct and its initializer too. Specifically, [class.union]/5:
A union of the form
union { member-specification } ;
is called an anonymous union; it defines an unnamed object of unnamed type. The member-specification of an anonymous union shall only define non-static data members. [Note: Nested types, anonymous unions, and functions cannot be declared within an anonymous union. —end note ]
So you cannot have the anonymous struct inside the anonymous union. You need to make one of them non-anonymous. For example:
struct A
{
struct AU { int a, b, c; };
union
{
AU a;
int vals[3];
};
};
which works with the initializer : A({x, x, x})
. The inconsistent behaviour around the A
initializer that you saw might be a gcc bug.
In addition to M.M's answer, according to the C++ union initialization rules the aggregate initializer always initializes the first union member only, which becomes the active member of that union.
So changing A
to be int vals[3]
the first declaration in the union
:
struct A
{
union
{
int vals[3];
struct { int a, b, c; };
};
};
or defining a constructor which initializes the member int vals[3]
instead of the aggregate initialization which initializes the first union
member:
struct A
{
A(int a, int b, int c)
: vals{ a, b c }
{}
union
{
struct { int a, b, c; };
int vals[3];
};
};
solves the problem of reading anonymous union
member int vals[3]
in a constexpr
expression.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With