c++ - 在具有引用字段的类上放置新的

标签 c++ language-lawyer c++20 stdlaunder

这是来自 C++20 规范 ( [basic.life]/8 ) 的代码示例:

struct C {
  int i;
  void f();
  const C& operator=( const C& );
};

const C& C::operator=( const C& other) {
  if ( this != &other ) {
    this->~C();              // lifetime of *this ends
    new (this) C(other);     // new object of type C created
    f();                     // well-defined
  }
  return *this;
}

int main() {    
  C c1;
  C c2;
  c1 = c2;   // well-defined
  c1.f();    // well-defined; c1 refers to a new object of type C
}

以下会是 法律 未定义的行为 :
struct C {
  int& i; // <= the field is now a reference
  void foo(const C& other) {
    if ( this != &other ) {
      this->~C();  
      new (this) C(other);  
    }
  }
};

int main() {
    int i = 3, j = 5;
    C c1 {.i = i};
    std::cout << c1.i << std::endl;
    C c2 {.i = j};
    c1.foo(c2);
    std::cout << c1.i << std::endl;
}

如果是 非法 , 会 std::launder让它合法?应该在哪里添加?

注: p0532r0 (page 5)在类似的情况下使用洗钱。

如果是 法律 ,它如何在没有“指针优化障碍”(即 std::launder )的情况下工作?我们如何避免编译器缓存 c1.i 的值?

该问题与关于 Implementability of std::optional 的旧 ISO 线程有关。 .

这个问题同样适用于一个常量字段(即,如果 i 中的 struct C 是: const int i )。

编辑

看来,正如@Language Lawyer 指出的那样in an answer below ,在 C++20 中更改了规则,以响应 RU007/US042 NB comments .

C++17 规范 [ptr.launder](第 21.6.4.4 节): ——强调我的——

[ Note: If a new object is created in storage occupied by an existing object of the same type, a pointer to the original object can be used to refer to the new object unless the type contains const or reference members; in the latter cases, this function can be used to obtain a usable pointer to the new object. ...— end note ]



规范中的 C++17 [ptr.launder] 代码示例(第 21.6.4.5 节):
struct X { const int n; };
X *p = new X{3};
const int a = p->n;
new (p) X{5}; // p does not point to new object (6.8) because X::n is const
const int b = p->n; // undefined behavior
const int c = std::launder(p)->n; // OK

C++20 [ptr.launder] 规范(第 17.6.4.5 节):

[ Note: If a new object is created in storage occupied by an existing object of the same type, a pointer to the original object can be used to refer to the new object unless its complete object is a const object or it is a base class subobject; in the latter cases, this function can be used to obtain a usable pointer to the new object. ...— end note ]



备注 这部分:

unless the type contains const or reference members;



出现在 C++17 中的内容在 C++20 中被删除,示例也相应更改。

规范中的 C++20 [ptr.launder] 代码示例(第 17.6.4.6 节):
struct X { int n; };
const X *p = new const X{3};
const int a = p->n;
new (const_cast<X*>(p)) const X{5}; // p does not point to new object ([basic.life])
                                    // because its type is const
const int b = p->n;                 // undefined behavior
const int c = std::launder(p)->n;   // OK

因此,显然有问题的代码在 C++20 中是合法的,而在 C++17 中它需要使用 std::launder访问新对象时。

开放问题:
  • 在 C++14 或之前(当 std::launder 不存在时)中的此类代码是什么情况?可能是 UB - 这就是为什么 std::launder被带到游戏中,对吧?
  • 如果在 C++20 中我们不需要 std::launder对于这种情况,编译器如何理解在没有我们的帮助(即没有“指针优化屏障”)的情况下对引用进行操作以避免缓存引用值?


  • 类似问题 here , here , herehere得到了矛盾的答案,有些人认为这是一种有效的语法,但建议重写它。我专注于语法的有效性和 std::launder 的需求(是或否) , 在不同的 C++ 版本中。

    最佳答案

    要回答当前 Unresolved 问题:

    第一个问题:

    • What is the case of such code in C++14 or before (when std::launder didn't exist)? Probably it is UB - this is why std::launder was brought to the game, right?


    是的,是UB。在@Language Lawyer 提到的 NB 问题中明确提到了这一点:

    Because of that issue all the standard libraries have undefined behaviors in widely used types. The only way to fix that issue is to adjust the lifetime rules to auto-launder the placement new. (https://github.com/cplusplus/nbballot/issues/7)



    第二个问题:

    If in C++20 we do not need std::launder for such a case, how the compiler can understand that the reference is being manipulated without our help (i.e. without "Pointer optimization barrier") to avoid caching of the reference value?



    如果在对象的两次使用之间调用了非常量成员函数,或者如果使用对象作为参数调用任何函数(通过引用传递),编译器已经知道不以这种方式优化对象(或子对象)值,因为这些功能可能会更改此值。对标准的这种更改只是增加了一些此类优化是非法的情况。

    关于c++ - 在具有引用字段的类上放置新的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62158678/

    相关文章:

    c++通过递归进行二进制搜索

    c++ - 伪析构函数调用不会破坏对象

    c++ - 为什么会出现错误 "no matching function for call to ' A(A<...auto...> )'"?

    c++ - C++20 范围适配器的递归应用导致编译时无限循环

    c++ - 非法使用未定义类型

    c++ - 打包应用程序是否比 C++ 更适合创建跨平台串行读/写应用程序以与我的 mbed 电子项目通信?

    c++ - 请让我理解将派生类对象分配给基类指针- C++

    c++ - gcov 不生成覆盖文件

    c++ - 删除 null void* 指针是未定义的行为吗?

    c++ - 内联命名空间和不明确的声明