簡體   English   中英

C++20 中的 std::launder 用例

[英]std::launder use cases in C++20

[1]

Are there any cases in which the addition of p0593r6 into C++20 ( § 6.7.2.11 Object model [intro.object] ) made std::launder not necessary, where the same use case in C++17 required std::launder , or它們是完全正交的嗎?


[2]

[ptr::launder]規范中的示例是:

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

@Nicol Bolas在此 SO answer中給出了另一個示例,使用指向有效存儲但類型不同的指針:

aligned_storage<sizeof(int), alignof(int)>::type data;
new(&data) int;
int *p = std::launder(reinterpret_cast<int*>(&data));

是否有其他用例,與允許投射兩個不可透明替換的對象無關,用於使用std::launder

具體來說:

  • reinterpret_cast從 A* 到 B*,兩者都是指針互轉換,在任何情況下都可能需要使用std::launder嗎? (即兩個指針可以指針互轉換但不能透明地替換嗎?規范在這兩個術語之間沒有關聯)。
  • reinterpret_castvoid*到 T* 是否需要使用std::launder
  • 下面的代碼是否需要使用std::launder 如果是這樣,在規范中的哪種情況下需要這樣做?

受此討論啟發的具有引用成員的結構:

struct A {
    constexpr A(int &x) : ref(x) {}
    int &ref;
};

int main() {
    int n1 = 1, n2 = 2;
    A a { n1 };
    a.~A();
    new (&a) A {n2};
    a.ref = 3; // do we need to launder somebody here?
    std::cout << a.ref << ' ' << n1 << ' ' << n2 << std::endl;
}

在 C++17 之前,具有給定地址和類型的指針始終指向位於該地址的該類型的 object,前提是代碼遵守 [basic.life] 的規則。 (請參閱: 自 C++17 以來,具有正確地址和類型的指針是否仍然始終是有效指針? )。

但是在 C++17 標准中為指針值添加了新的質量。 這種質量不是在指針類型中編碼,而是直接限定值,與類型無關(這也是可追溯性的情況)。 它在[basic.compound]/3中有描述

指針類型的每個值都是以下之一:

  • 指向 object 或 function 的指針(據說該指針指向 object 或函數),或

  • 超過 object ([expr.add]) 末尾的指針,或

  • 該類型的 null 指針值,或無效的指針值。

指針值的這種性質有其自己的語義(轉換規則),對於reinterpret_cast的情況,它在下一段中描述:

如果兩個對象是指針可互轉換的,那么它們具有相同的地址,並且可以通過 reinterpret_cast 從指向另一個的指針獲得指向其中一個的指針

在 [basic-life] 中,我們可以找到另一條規則,該規則描述了在重用 object 存儲時如何轉換此質量:

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指原 object,或原 object 的名稱將自動指新的 object 和 [...]

如您所見,質量“指向對象的指針”附加到特定的 object。

這意味着在您給出的第一個示例的以下變體中, reinterpret_cast不允許我們不使用指針優化屏障:

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 = *reinterpret_cast <int*> (p);                 // undefined behavior
const int c = *std::launder(reinterpret_cast <int*> (p)); 

reinterpret_cast不是指針優化屏障: reinterpret_cast <int*>(p)指向被破壞的 object 的成員。

設想它的另一種方式是,只要 object 是指針可相互轉換的,或者如果它被強制轉換為 void,然后再返回到指針可相互轉換的類型,那么reinterpret_cast就可以保存“指向”質量。 (見[exp.static_cast]/13 )。 所以reinterpret_cast <int*>(reinterpret_cast <void*>(p))仍然指向被破壞的 object。

對於您給出的最后一個示例,名稱a指的是非常量完整的 object,因此原始a可以透明地替換為新的 object。


對於您問的第一個問題:“是否有任何情況下將 p0593r6 添加到 C++20 中(§ 6.7.2.11 Object model [intro.object])不需要 std::launder 中的相同用例C++17 需要 std::launder,還是它們完全正交?”

老實說,我找不到任何 std::launder 可以補償隱含生命周期對象的情況。 但我發現一個例子是隱式生命周期 object 使 std::launder 有用:

  class my_buffer {
      alignas(int) std::byte buffer [2*sizeof(int)];
      
      int * begin(){
         //implictly created array of int inside the buffer
         //nevertheless to get a pointer to this array, 
         //std::launder is necessary as the buffer is not
         //pointer inconvertible with that array
         return *std::launder (reinterpret_cast <int(*)[2]>(&buffer));
         }
      create_int(std::size_t index, int value){
         new (begin()+index) auto{value};
         }
       };
      

暫無
暫無

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

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