c++ - 什么时候可以安全地重用来自可简单破坏的对象的内存而不进行清洗
问题描述
关于以下代码:
class One {
public:
double number{};
};
class Two {
public:
int integer{};
}
class Mixture {
public:
double& foo() {
new (&storage) One{1.0};
return reinterpret_cast<One*>(&storage)->number;
}
int& bar() {
new (&storage) Two{2};
return reinterpret_cast<Two*>(&storage)->integer;
}
std::aligned_storage_t<8> storage;
};
int main() {
auto mixture = Mixture{};
cout << mixture.foo() << endl;
cout << mixture.bar() << endl;
}
我没有为这些类型调用析构函数,因为它们很容易被破坏。我对标准的理解是,为了安全起见,我们需要在将指向存储的指针传递给reinterpret_cast
. 但是,libstdc++ 中的 std::optional 实现似乎没有使用std::launder()
,只是将对象直接构造到联合存储中。 https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/std/optional。
我上面的例子是明确定义的行为吗?我需要做什么才能使其工作?工会会做这项工作吗?
解决方案
在您的代码中,您确实需要std::launder
做reinterpret_cast
您想做的事情。这是与重用内存不同的问题。根据标准([expr.reinterpret].cast]7),你的表达
reinterpret_cast<One*>(&storage)
相当于:
static_cast<One*>(static_cast<void*>(&storage))
但是,外部static_cast
没有成功生成指向新创建One
对象的指针,因为根据 [expr.static.cast]/13,
如果原始指针值指向对象a,并且有一个类型为b
T
的对象(忽略 cv 限定)与a指针可互转换(6.9.2) ,则结果是指向b的指针。否则,指针值不会因转换而改变。
也就是说,结果指针仍然指向storage
对象,而不是One
嵌套在其中的对象,并且将其用作指向One
对象的指针将违反严格的别名规则。您必须使用std::launder
来强制结果指针指向该One
对象。或者,正如评论中所指出的,您可以直接使用placement new 返回的指针,而不是从reinterpret_cast
.
如果按照评论中的建议,您使用了 union 而不是aligned_storage
,
union {
One one;
Two two;
};
您将回避指针互转换问题,因此std::launder
由于非指针互转换而不需要。但是,仍然存在内存重用的问题。在这种特殊情况下,std::launder
由于重用而不需要,因为您的One
和Two
类不包含任何const
-qualified 或引用类型 ([basic.life]/8) 的非静态数据成员。
最后,还有一个问题,为什么 libstdc++ 的实现std::optional
不使用std::launder
,即使可能包含包含-qualified 或引用类型std::optional
的非静态数据成员的类。const
正如评论中所指出的,libstdc++ 是实现的一部分,std::launder
当实现者知道 GCC 仍然可以在没有它的情况下正确编译代码时,可能会简单地忽略它。导致引入的讨论std::launder
(参见CWG 1776和链接的线程,N4303,P0137)似乎表明,在比我更了解标准的人看来,std::launder
确实需要为了使基于联合的实现std::optional
在存在const
-qualified 或 reference 类型的成员时定义良好。但是,我不确定标准文本是否足够清晰以使这一点显而易见,并且可能值得讨论如何澄清它。
推荐阅读
- python - 为稀疏的 64 位无符号整数创建最小完美哈希
- c# - 转换位图 tp 图像
- android - java.lang.UnsatisfiedLinkError: 找不到 java.lang.String com.swoop.utility.ImageStabilizer.hello() 的实现
- php - 通过 MySQL 和 PDO 使用 PHP 的多级嵌套 JSON 输出
- sql-server - 在 SQL Server Management Studio (SSMS) 中关闭自动提交
- git - Git 存储库,其中脚本物理上位于另一个 git 存储库中
- python - 在打印命令中打印字符串时出现 Discord.py 错误
- f# - F# - 包含数字的字符串的自然排序
- php - 尝试使用 Laravel 迁移添加外键约束时出错
- arrays - Ajax JSON 请求对象包含数组,但该数组在服务器端未定义。字符串发送良好