这是来自 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 (page 5)在类似的情况下使用洗钱。
如果是 法律 ,它如何在没有“指针优化障碍”(即
std::launder
)的情况下工作?我们如何避免编译器缓存 c1.i
的值?该问题与关于 Implementability of
std::optional
的旧 ISO 线程有关。 .这个问题同样适用于一个常量字段(即,如果
i
中的 struct C
是: const int i
)。编辑
看来,正如@Language Lawyer 指出的那样in an answer below ,在 C++20 中更改了规则,以响应 RU007/US042 NB comments .
C++17 规范 [ptr.launder](第 21.6.4.4 节): ——强调我的——
[ 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; in the latter cases, this function can be used to obtain a usable pointer to the new object. ...— end note ]
规范中的 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 subobject; in the latter cases, this function can be used to obtain a usable pointer to the new object. ...— end note ]
备注 这部分:
unless the type contains const or reference members;
出现在 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 中它需要使用
std::launder
访问新对象时。开放问题:
std::launder
不存在时)中的此类代码是什么情况?可能是 UB - 这就是为什么 std::launder
被带到游戏中,对吧? std::launder
对于这种情况,编译器如何理解在没有我们的帮助(即没有“指针优化屏障”)的情况下对引用进行操作以避免缓存引用值? 类似问题 here , here , here和 here得到了矛盾的答案,有些人认为这是一种有效的语法,但建议重写它。我专注于语法的有效性和
std::launder
的需求(是或否) , 在不同的 C++ 版本中。
最佳答案
要回答当前 Unresolved 问题:
第一个问题:
- What is the case of such code in C++14 or before (when std::launder didn't exist)? Probably it is UB - this is why std::launder was brought to the game, right?
是的,是UB。在@Language Lawyer 提到的 NB 问题中明确提到了这一点:
Because of that issue all the standard libraries have undefined behaviors in widely used types. The only way to fix that issue is to adjust the lifetime rules to auto-launder the placement new. (https://github.com/cplusplus/nbballot/issues/7)
第二个问题:
If in C++20 we do not need std::launder for such a case, how the compiler can understand that the reference is being manipulated without our help (i.e. without "Pointer optimization barrier") to avoid caching of the reference value?
如果在对象的两次使用之间调用了非常量成员函数,或者如果使用对象作为参数调用任何函数(通过引用传递),编译器已经知道不以这种方式优化对象(或子对象)值,因为这些功能可能会更改此值。对标准的这种更改只是增加了一些此类优化是非法的情况。
关于c++ - 在具有引用字段的类上放置新的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62158678/