这是来自 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 (第 5 页)针对类似情况使用洗钱。
如果它是合法的,它如何在没有“指针优化障碍”(即std::launder
)的情况下工作?我们如何避免编译器缓存 的值c1.i
?
该问题与关于std::optional
.
该问题也非常相似地适用于常量字段(即,如果上面i
是struct C
:)const int i
。
编辑
正如@Language Lawyer在下面的答案中指出的那样,似乎在 C++20 中规则已更改,以响应RU007/US042 NB 评论。
C++17 规范 [ptr.launder] (§ 21.6.4.4): --emphasis mine --
[注:如果在同类型的现有对象占用的存储空间中创建新对象,则可以使用指向原始对象的指针来引用新对象,除非该类型包含 const 或引用成员;在后一种情况下,此函数可用于获取指向新对象的可用指针。……结束注]
规范中的 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):
[注:如果在已存在的同类型对象占用的存储空间中创建新对象,则可以使用指向原始对象的指针来引用新对象,除非其完整对象是const对象或基类子对象;在后一种情况下,此函数可用于获取指向新对象的可用指针。……结束注]
注意部分:
除非该类型包含 const 或引用成员;
出现在 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
在访问新对象时使用。
开放式问题:
在 C++14 或之前(当
std::launder
不存在时)这样的代码是什么情况?可能是 UB - 这就是std::launder
被带到游戏中的原因,对吧?如果在 C++20 中我们不需要
std::launder
这种情况,编译器如何理解在没有我们帮助的情况下正在操纵引用(即没有“指针优化屏障”)以避免缓存引用值?
类似的问题here , here , here和here得到了相互矛盾的答案,有些人认为这是一种有效的语法,但建议重写它。std::launder
在不同的 C++ 版本中,我专注于语法的有效性和对 的需求(是或否) 。