这是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节):-强调我的-
规范中的 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节):
注意该部分:
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得到了矛盾的答案,有些人认为这是有效的语法,但建议重写它。我主要关注语法的有效性以及在不同C++版本中对
std::launder
的需要(是或否)。 最佳答案
要回答当前 Unresolved 问题:
第一个问题:
是的,那是UB。 NB问题@Language Lawyer中明确提到了这一点:
第二个问题:
如果在两次使用该对象之间调用了非常量成员函数,或者以该对象作为参数调用了任何函数(通过by-ref传递),则编译器已经知道不会以这种方式优化对象(或子对象)的值,因为该值可能会被这些功能更改。对该标准的更改仅增加了一些其他情况,其中这种优化是非法的。