[英]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
?
具體來說:
std::launder
嗎? (即兩個指針可以指針互轉換但不能透明地替換嗎?規范在這兩個術語之間沒有關聯)。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.