Ami*_*rsh 7 c++ language-lawyer c++20 stdlaunder
[1]
\n\n是否有任何情况不需要将p0593r6添加到 C++20 ( \xc2\xa7 6.7.2.11对象模型[intro.object] ) std::launder,而需要 C++17 中的相同用例std::launder,或者它们是完全正交?
[2]
\n\n[ptr::launder]规范中的示例是:
\n\nstruct X { int n; };\nconst X *p = new const X{3};\nconst int a = p->n;\nnew (const_cast<X*>(p)) const X{5}; // p does not point to new object ([basic.life]) because its type is const\nconst int b = p->n; // undefined behavior\nconst int c = std::launder(p)->n; // OK\nRun Code Online (Sandbox Code Playgroud)\n\n@Nicol Bolas在这个 SO 答案中给出了另一个例子,使用指向有效存储但类型不同的指针:
\n\naligned_storage<sizeof(int), alignof(int)>::type data;\nnew(&data) int;\nint *p = std::launder(reinterpret_cast<int*>(&data));\nRun Code Online (Sandbox Code Playgroud)\n\n是否还有其他用例,与允许铸造两个不可透明替换的对象无关,以供使用std::launder?
具体来说:
\n\nstd::launder,在任何情况下都可能需要使用?(即两个指针可以是指针可相互转换的,但不能透明地替换吗?规范在这两个术语之间没有关联)。std::launder?std::launder?如果是这样,规范中在什么情况下会要求这样做?受此讨论启发的带有参考成员的结构:
\n\nstruct A {\n constexpr A(int &x) : ref(x) {}\n int &ref;\n};\n\nint main() {\n int n1 = 1, n2 = 2;\n A a { n1 };\n a.~A();\n new (&a) A {n2};\n a.ref = 3; // do we need to launder somebody here?\n std::cout << a.ref << \' \' << n1 << \' \' << n2 << std::endl;\n}\nRun Code Online (Sandbox Code Playgroud)\n
在 C++17 之前,具有给定地址和类型的指针始终指向位于该地址的该类型的对象,前提是代码遵循 [basic.life] 的规则。(请参阅:自 C++17 以来,具有正确地址和类型的指针是否仍然始终是有效指针?)。
\n但在 C++17 标准中为指针值添加了新的特性。这种质量不在指针类型内进行编码,而是直接限定值,与类型无关(可追溯性也是如此)。它在[basic.compound]/3中描述
\n\n\n指针类型的每个值都是以下之一:
\n\n
\n- \n
指向对象或函数的指针(该指针被称为指向对象或函数),或者
\n- \n
超过对象末尾的指针 ([expr.add]),或
\n- \n
该类型的空指针值,或\n无效的指针值。
\n
reinterpret_cast指针值的这种性质有其自己的语义(转换规则),下一段描述了它的情况:
\n\n如果两个对象是指针可相互转换的,那么它们具有相同的地址,并且可以通过reinterpret_\xc2\xadcast从指向另一个对象的指针获得指向另一个对象的指针。
\n
在[basic-life]中,我们可以找到另一个规则,描述了当对象存储被重用时如何转变这种质量:
\n\n\n如果一个对象的生命周期结束后,在该对象占用的存储空间被重用或释放之前,在原对象占用的存储位置上创建了一个新对象,一个指向原对象的指针,一个指向该对象的引用引用原始对象,或者原始对象的名称将自动引用新对象,并且,[...]
\n
正如您所看到的,“指向对象的指针”质量附加到特定对象。
\n这意味着在您给出的第一个示例的下面的变体中,reinterpret_cast不允许我们不使用指针优化屏障:
struct X { int n; };\nconst X *p = new const X{3};\nconst int a = p->n;\nnew (const_cast<X*>(p)) const X{5}; // p does not point to new object ([basic.life]) because its type is const\nconst int b = *reinterpret_cast <int*> (p); // undefined behavior\nconst int c = *std::launder(reinterpret_cast <int*> (p)); \nRun Code Online (Sandbox Code Playgroud)\nAreinterpret_cast不是指针优化障碍:reinterpret_cast <int*>(p)指向被破坏对象的成员。
另一种设想方式是,只要reinterpret_cast对象是指针可相互转换的,或者将其强制转换为 void,然后返回到指针可相互转换类型,则“指针”质量就会得到保留。(参见[exp.static_cast]/13)。所以reinterpret_cast <int*>(reinterpret_cast <void*>(p))仍然指向被破坏的对象。
对于您给出的最后一个示例,该名称a引用一个非常量完整对象,因此原始对象a可以透明地由新对象替换。
对于您问的第一个问题:“是否存在将 p0593r6 添加到 C++20 (\xc2\xa7 6.7.2.11 对象模型 [intro.object]) 中使得 std::launder 不必要的情况,其中相同C++17 中的用例需要 std::launder,还是它们完全正交?”
\n老实说,我还没有找到任何 std::launder 可以补偿隐式生命周期对象的情况。但我发现一个例子是隐式生命周期对象使 std::launder 有用:
\n class my_buffer {\n alignas(int) std::byte buffer [2*sizeof(int)];\n \n int * begin(){\n //implictly created array of int inside the buffer\n //nevertheless to get a pointer to this array, \n //std::launder is necessary as the buffer is not\n //pointer inconvertible with that array\n return *std::launder (reinterpret_cast <int(*)[2]>(&buffer));\n }\n create_int(std::size_t index, int value){\n new (begin()+index) auto{value};\n }\n };\n \nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
2009 次 |
| 最近记录: |