简体   繁体   English

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

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

[1] [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 are they completely orthogonal ? 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] [2]

The example in the spec for [ptr::launder] is: [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

Another example is given by @Nicol Bolas in this SO answer , using a pointer that points to a valid storage but of a different type: @Nicol Bolas在此 SO answer中给出了另一个示例,使用指向有效存储但类型不同的指针:

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

Are there other use cases, not related to allowing casting of two objects which are nottransparently replaceable , for using std::launder ?是否有其他用例,与允许投射两个不可透明替换的对象无关,用于使用std::launder

Specifically:具体来说:

  • Would reinterpret_cast from A* to B*, both are pointer-interconvertible , may require using std::launder in any case? reinterpret_cast从 A* 到 B*,两者都是指针互转换,在任何情况下都可能需要使用std::launder吗? (ie can two pointers be pointer-interconvertible and yet not be transparently replaceable ? the spec didn't relate between these two terms). (即两个指针可以指针互转换但不能透明地替换吗?规范在这两个术语之间没有关联)。
  • Does reinterpret_cast from void* to T* require using std::launder ? reinterpret_castvoid*到 T* 是否需要使用std::launder
  • Does the following code below require use of std::launder ?下面的代码是否需要使用std::launder If so, under which case in the spec does it fall to require that?如果是这样,在规范中的哪种情况下需要这样做?

A struct with reference member, inspired by this discussion :受此讨论启发的具有引用成员的结构:

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

Before C++17, a pointer with a given address and type always pointed to an object of that type located at that address, provided that the code respects the rules of [basic.life].在 C++17 之前,具有给定地址和类型的指针始终指向位于该地址的该类型的 object,前提是代码遵守 [basic.life] 的规则。 (see: Is a pointer with the right address and type still always a valid pointer since C++17? ). (请参阅: 自 C++17 以来,具有正确地址和类型的指针是否仍然始终是有效指针? )。

But in the C++17 standard added a new quality to a pointer value.但是在 C++17 标准中为指针值添加了新的质量。 This quality is not encode within the pointer type but qualifies directly the value, independently of the type (this is the case also of the traceability).这种质量不是在指针类型中编码,而是直接限定值,与类型无关(这也是可追溯性的情况)。 It is described in [basic.compound]/3它在[basic.compound]/3中有描述

Every value of pointer type is one of the following:指针类型的每个值都是以下之一:

  • a pointer to an object or function (the pointer is said to point to the object or function), or指向 object 或 function 的指针(据说该指针指向 object 或函数),或

  • a pointer past the end of an object ([expr.add]), or超过 object ([expr.add]) 末尾的指针,或

  • the null pointer value for that type, or an invalid pointer value.该类型的 null 指针值,或无效的指针值。

This quality of a pointer value has its own semantic (transition rules), and for the case of reinterpret_cast it is described in the next paragraph:指针值的这种性质有其自己的语义(转换规则),对于reinterpret_cast的情况,它在下一段中描述:

If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast.如果两个对象是指针可互转换的,那么它们具有相同的地址,并且可以通过 reinterpret_cast 从指向另一个的指针获得指向其中一个的指针

In [basic-life], we can find an other rule that describes how transitions this quality when an object storage is reused:在 [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 referred to the original object, or the name of the original object will automatically refer to the new object and, [...] 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 和 [...]

As you can see the quality "pointer to an object" is attached to a specific object.如您所见,质量“指向对象的指针”附加到特定的 object。

That means that in the variation bellow of the first example you give, the reinterpret_cast does not allow us not to use the pointer optimization barrier:这意味着在您给出的第一个示例的以下变体中, 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)); 

A reinterpret_cast is not a pointer optimization barrier: reinterpret_cast <int*>(p) points to the member of the destroyed object. reinterpret_cast不是指针优化屏障: reinterpret_cast <int*>(p)指向被破坏的 object 的成员。

An other way to conceive it is that the "pointer to" quality is conserved by reinterpret_cast as long as the object are pointer inter-convertible or if its casted to void and then back to a pointer inter-convertible type.设想它的另一种方式是,只要 object 是指针可相互转换的,或者如果它被强制转换为 void,然后再返回到指针可相互转换的类型,那么reinterpret_cast就可以保存“指向”质量。 (See [exp.static_cast]/13 ). (见[exp.static_cast]/13 )。 So reinterpret_cast <int*>(reinterpret_cast <void*>(p)) still points to the destroyed object.所以reinterpret_cast <int*>(reinterpret_cast <void*>(p))仍然指向被破坏的 object。

For the last example you gives, the name a refers to a non const complete object, so the original a is transparently replaceable by the new object.对于您给出的最后一个示例,名称a指的是非常量完整的 object,因此原始a可以透明地替换为新的 object。


For the first question you ask: "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 are they completely orthogonal?"对于您问的第一个问题:“是否有任何情况下将 p0593r6 添加到 C++20 中(§ 6.7.2.11 Object model [intro.object])不需要 std::launder 中的相同用例C++17 需要 std::launder,还是它们完全正交?”

Honestly, I have not been able to find any cases that where std::launder could compensate implict-lifetime objects.老实说,我找不到任何 std::launder 可以补偿隐含生命周期对象的情况。 But I found an example were implicit-lifetime object makes std::launder usefull:但我发现一个例子是隐式生命周期 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