Clash when using trivially copied / moved union in constexpr

Given the following code

struct S { constexpr S() = default; constexpr S(S const &) = default; constexpr S(S &) = default; constexpr S(S &&) = default; #if 1 S & operator = (S const &) = default; S & operator = (S &) = default; S & operator = (S &&) = default; #else constexpr S & operator = (S const &) = default; constexpr S & operator = (S &) = default; constexpr S & operator = (S &&) = default; #endif ~S() = default; }; struct U { union { S s; }; constexpr U() : s{} { ; } }; inline constexpr bool test() { U v; U w; v = w; return true; } static_assert(test()); 

I found that there is a contradiction.

I want to use union (or euqally union-like class) in constexpr .

Version #if 1 gives an error ( error: constexpr function never produces a constant expression ):

 main.cpp:31:23: error: constexpr function never produces a constant expression [-Winvalid-constexpr] inline constexpr bool test() ^ main.cpp:35:7: note: non-constexpr function 'operator=' cannot be used in a constant expression v = w; ^ main.cpp:19:8: note: declared here struct U ^ main.cpp:39:15: error: static_assert expression is not an integral constant expression static_assert(test()); ^~~~~~ main.cpp:35:7: note: non-constexpr function 'operator=' cannot be used in a constant expression v = w; ^ main.cpp:39:15: note: in call to 'test()' static_assert(test()); ^ main.cpp:19:8: note: declared here struct U ^ 2 errors generated. 

But version #if 0 also gives an error ( error: defaulted definition of copy assignment operator is not constexpr ):

 main.cpp:12:5: error: defaulted definition of copy assignment operator is not constexpr constexpr S & operator = (S const &) = default; ^ main.cpp:13:5: error: defaulted definition of copy assignment operator is not constexpr constexpr S & operator = (S &) = default; ^ main.cpp:14:5: error: defaulted definition of move assignment operator is not constexpr constexpr S & operator = (S &&) = default; ^ main.cpp:35:7: error: object of type 'U' cannot be assigned because its copy assignment operator is implicitly deleted v = w; ^ main.cpp:24:11: note: copy assignment operator of 'U' is implicitly deleted because field 's' has no copy assignment operator S s; ^ 4 errors generated. 

LIVE EXAMPLE

Everything is higher for clang 3.7.0 version clang 3.7.0 , but the newer version of clang 3.8.0 (trunk 253951) does not say anything about constexpr S & operator = (S const &) = default; (it allows you to compile such code), but the consequences are the same.

For trivial copy / move constructor, but user-provided assignment operators are exactly the same as code (build).

I think the problem (error) in the user-declared special functions, but it should be legal in general.

Also pay attention to the last and last, but one section of this code . A fairly well-designed (in my opinion) variant becomes useless in constexpr due to differences in the compiler's interpretation of the following definitions:

 struct S {}; 

and

 struct S { constexpr S() = default; constexpr S(S const &) = default; constexpr S(S &) = default; constexpr S(S &&) = default; S & operator = (S const &) = default; S & operator = (S &) = default; S & operator = (S &&) = default; ~S() = default; }; 

But both are equivalent in my mind (the second is recognized by the compiler, not POD, but who needs it?).

Why is the default empty assignment operator struct not constexpr ?

UPDATE:

Removing the destructor declaration causes the problem to disappear. Thus, even a user-declared (not user-provided) destructor makes implicitly defined assignment operators not marked as constexpr and be nontrivial. This is definitely a mistake, because no source talks about it.

+5
source share

Source: https://habr.com/ru/post/1237155/


All Articles