使用construct_at更改活动constexpr联合成员

发布于 2025-01-17 11:41:59 字数 1628 浏览 1 评论 0原文

我正在尝试使用construps_at来更改constexpr Union的活动成员,并在使用initializer list vs.成员的构造函数初始化其成员时获取以下错误。有人可以解释为什么吗?

#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);
}

错误:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

開玄 2025-01-24 11:41:59

虽然我不知道工作/非工作ctors的确切情况,但您有一个constexpr u u(10);,您以后尝试通过调用调整功能(CTOR)来调用它std :: struct_at(&amp; uz,10);。您期望尝试修改constexpr对象有什么期望?在u对象上删除constexpr,它不会使您的函数降低constexpr

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.

凡间太子 2025-01-24 11:41:59

正如另一个答案中所述,u 上的 constexpr 在这里是错误的。但我会尝试用您的代码来说明编译器的行为。

std::construct_at 将在给定的存储位置构造一个新对象。然而,constexpr 意味着 const,这意味着您正在尝试在具有自动存储持续时间的 const 完整对象中创建新对象。这是不允许的,并且会导致未定义的行为。 (然而,对 std::construct_at 的调用并不是格式错误的。它可以使用 const 指针来调用。)

由于这种情况发生在表达式求值期间,要求是常量表达式,那么问题是这是否使该表达式不是常量表达式,即编译器是否必须诊断未定义的行为。

一般来说,任何核心语言的未定义行为都必须由编译器诊断。编译器可以诊断任何标准库未定义的行为。

因此,问题在于 std::construct_at 调用的计数。函数本身被指定为相当于相应的placement-new ([specialized .construct]/2),但有一个特殊例外,即常量表达式中允许使用 std::construct_at,除非“底层构造函数调用取消了 [表达式] 的资格”从成为核心常量表达式” ([expr.const]/6.1< /a>)。

虽然我认为这不是很清楚,但我猜测这应该算作核心语言未定义行为,因为它没有违反库子句中的任何前提条件或声明的未定义行为,而是核心语言子句中声明的行为。

这样,表达式 func() == 1 在两个版本的代码中都不会是常量表达式,并且编译器应该发出诊断,例如 GCC 在至少一个版本中所做的诊断显示的案例。

然而,正如我上面所暗示的,我不确定我对常量表达式要求的解释是否是这里预期的。

我猜想 [expr.const]/6.1 也可以被读取,这样未定义的行为要求根本不适用于 std::construct_at 调用,而仅适用于底层构造函数调用。但这似乎确实是无意的。

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.

帅冕 2025-01-24 11:41:59

我错了,这不相关。

正如评论中提到的 ,它适用于最新版本的 clang。最初在 C++11 中不允许在 constexpr 构造函数中进行赋值(检查第 7.1.5 节中的 $4),但在我看来,在 C++14 (同一部分)中是允许的N3652。检查(7.1.5)/3和(7.1.5)/4的相关更改。因此,如果您使用 C++14 和 GCC 进行构建,那么您会遇到编译器错误。


这个答案很大程度上基于 另一个答案

I was wrong, this is not related.

As it is mentioned in the comments, it works with recent versions of clang. Initially assignment in constexpr constructors weren't allowed in C++11 (check $4 in section 7.1.5), but were allowed in C++14 (same section) in my opinion by N3652. Check the related changes for (7.1.5)/3 and (7.1.5)/4. So if you are building with C++14 with GCC, then you are struck by a compiler bug.

This answer is heavily based on another SO answer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文