Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

changing active constexpr union member using construct_at

I'm trying to change the active member of a constexpr union using construct_at and get the following error when constructor initializes it's member using initializer list vs. member. Can someone explain why?

#include <memory>
struct Z { 
 #if 1  // If this changes to zero it does not compile   
   constexpr Z(int x) : y(x){      
   }  
  #else  
     constexpr Z(int x) {
         y = x;      
   }  
  #endif
   int y;
};

struct W { 
   constexpr W(int x) {
      y = x;
   }   
   W(const W&) {}
   int y;
};

union U { 
   Z z;
   W w;
   constexpr U(int z) : w(z) {
   }   
};

constexpr int func() {
   constexpr U u(10);
   std::construct_at(&u.z, 10);
//   ::new (&u.z) Z(10);
   return u.z.y;
}

int main() {
    static_assert(func() == 1);
}

Error:

source>: In function 'int main()':
<source>:37:26: error: non-constant condition for static assertion
   37 |     static_assert(func() == 10);
      |                   ~~~~~~~^~~~~
<source>:37:23:   in 'constexpr' expansion of 'func()'
<source>:31:21:   in 'constexpr' expansion of 'std::construct_at<const Z, int>((& u.U::z), 10)'
/opt/compiler-explorer/gcc-11.2.0/include/c++/11.2.0/bits/stl_construct.h:97:14:   in 'constexpr' expansion of '((Z*)<anonymous>)->Z::Z(<anonymous>)'
<source>:8:12: error: modifying a const object '((Z*)this)->Z::y' is not allowed in a constant expression
    8 |          y = x;
      |          ~~^~~
<source>:30:16: note: originally declared 'const' here
   30 |    constexpr U u(10);
      |                ^
Compiler returned: 1
like image 477
user3882729 Avatar asked Oct 19 '25 04:10

user3882729


2 Answers

While I don't know what exactly the case with working/non-working ctors, you have a constexpr U u(10); on which you later try to call a modifying function (ctor) by calling std::construct_at(&u.z, 10);. What do you expect from trying to modify a constexpr object? Remove constexpr on the u object, it won't make your function less constexpr.

like image 62
ixSci Avatar answered Oct 21 '25 19:10

ixSci


As noted in the other answer, constexpr on u is wrong here. But I will try to say something about the compiler behavior with your code.

std::construct_at will construct a new object at the given storage location. However constexpr implies const, meaning you are trying to create a new object in a const complete object with automatic storage duration. That is not allowed and causes undefined behavior. (The call to std::construct_at is however not ill-formed. It may be called with a const pointer.)

Since this is happening during the evaluation of an expression that is required to be a constant expression, the question is then whether this makes the expression not a constant expression, i.e. whether the compiler has to diagnose the undefined behavior.

Generally any core language undefined behavior must be diagnosed by the compiler. Any standard library undefined behavior may be diagnosed by the compiler.

So the question would be here as which the std::construct_at call counts. The function itself is specified as equivalent to the corresponding placement-new ([specialized.construct]/2), except that a special exception is made that std::construct_at is allowed in a constant expression unless "the underlying constructor call disqualifies [the expression] from being a core constant expression" ([expr.const]/6.1).

Although I don't think it is really clear, I would guess that this is supposed to count as core language undefined behavior since it is not violating any precondition or stated undefined behavior in the library clauses, but rather one stated in the core language clauses.

With that the expression func() == 1 would not be a constant expression in either of the two versions of the code and the compiler should put out a diagnostic such as GCC is doing in at least one of the shown cases.

However, as I hinted above, I am not confident that my interpretation of the constant expression requirements is the intended one here.

I guess [expr.const]/6.1 could also be read such that the undefined behavior requirement does not apply to a std::construct_at call at all, but only to the underlying constructor call. But that does really seem unintended.

like image 27
user17732522 Avatar answered Oct 21 '25 18:10

user17732522



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!