簡體   English   中英

在帶有參考字段的 class 上放置新

[英]placement new on a class with reference field

這是來自 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 (第 5 頁)在類似情況下使用洗錢。

如果它是合法的,它如何在沒有“指針優化障礙” (即std::launder )的情況下工作? 我們如何避免編譯器緩存c1.i的值?

該問題與關於std::optional實施性的舊 ISO 線程有關。

這個問題也同樣適用於常量字段(即,如果struct C中的上述i為: const int i )。


編輯

正如@Language Lawyer 在下面的回答中指出的那樣,似乎在 C++20 中規則已更改,以響應RU007/US042 NB 評論

C++17 規格 [ptr.launder] (§ 21.6.4.4): --emphasis mine --

[ 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; 在后一種情況下,此 function 可用於獲取指向新 object 的可用指針。 ......結束注]

規范中的 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子對象; 在后一種情況下,此 function 可用於獲取指向新 object 的可用指針。 ...-結束注]

注意部分:

除非該類型包含 const 或引用成員;

出現在 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,它需要在訪問新的 object 時使用std::launder


開放式問題:

  • C++14 或之前的代碼是什么情況(當std::launder不存在時)? 可能是 UB - 這就是為什么將std::launder帶入游戲的原因,對吧?

  • 如果在 C++20 中我們不需要std::launder這種情況,編譯器如何理解在沒有我們幫助的情況下正在操縱引用(即沒有“指針優化屏障” )以避免緩存引用值?


類似的問題here , here , herehere得到了相互矛盾的答案,有些人認為這是一種有效的語法,但建議重寫它。 在不同的 C++ 版本中,我專注於語法的有效性和std::launder的需要(是或否)。

用 const 限定和引用非靜態數據成員替換對象是合法的。 而現在,在 C++20 中,[name of|a [pointer|reference] to] 原始 object 將指代替換后的新 object。 規則已根據 RU007/US042 NB 評論http://wg21.link/p1971r0#RU007 更改

RU007。 [basic.life].8.3 放寬指針值/別名規則

...

將 6.7.3 [basic.life] 項目符號 8.3 更改如下:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • ...

  • 原始 object 的類型不是 const 限定的,並且,如果是 class 類型,則不包含其類型為 const 限定的任何非靜態數據成員或既不是完整的 ZA8CFDE6331BD59EB2AC96F8911Z限定的引用類型,也不是完整的 ZA8CFDE6331BD59EB2AC96F891Z 限定C46這樣一個 object ,和

  • ...

要回答當前懸而未決的問題:

第一個問題:

  • C++14 或之前的代碼是什么情況(當 std::launder 不存在時)? 可能是 UB - 這就是為什么將 std::launder 帶入游戲的原因,對吧?

是的,是UB。 這在@Language Lawyer 提到的NB 問題中明確提到:

由於這個問題,所有標准庫在廣泛使用的類型中都有未定義的行為。 解決該問題的唯一方法是調整生命周期規則以自動清洗新的展示位置。 https://github.com/cplusplus/nbballot/issues/7

第二個問題:

如果在 C++20 中我們不需要 std::launder 這種情況,編譯器如何理解在沒有我們幫助的情況下(即沒有“指針優化屏障”)正在操縱引用以避免緩存引用值?

Compilers already know to not optimize object (or sub-object) value this way if a non-const member function was called between two usages of the object or if any function was called with the object as a parameter (passed by-ref),因為這個值可能會被那些函數改變。 對標准的這種更改只是增加了一些此類優化是非法的情況。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM