yat*_*nai 0 c++ placement-new c++20 bit-cast
我对绕过二进制类型转换限制的方法感兴趣,因为 bit_cast 使用复制到堆栈上的变量,这不是很快。但据我所知,普通类型不会在默认构造函数中初始化内存。这是否意味着,对于将 bit_casting 转换为普通类型,我们可以使用放置 new 和指向我们感兴趣的内存的指针,从而保持内存中的字节不变并避免不必要的复制?在这个问题上是否有我找不到的标准立场,并且禁止我将新的位置与一些“非主题”指针一起使用?
例如,第一个选项是其他人如何使用此功能。第二个选项是我想如何使用它。
#include <iostream>
using namespace std;
class Foo
{
private: int __unused0 : 31; //A little bit of binary magic
private: int __unused1 : 1;
public: Foo() : __unused0(1), __unused1(1) {} //We're still saving trivially_copyable, so we're good
};
int main()
{
//char bytes[1024];
//auto* foo = new(bytes) Foo;
Foo bytes{};
auto* foo = new(&bytes) int;
cout << *foo;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
UPD:这一切的目的是在不更改容器的情况下处理内存中的原始字节。在最简单的情况下,我需要像“检查寄存器中的高位”这样的小型优化,这是通过单个汇编命令来实现的,例如[x < 0]在处理结构中的位字段时创建一系列命令,例如[x & 0x80][x >> 7][x == 0].
UPD2:我对在代码中使用新放置的可能性感兴趣,因为从语义的角度来看。当使用 bit_cast 时,我们将一个对象复制到另一个对象中,当使用时,new我们使用对象 A 的内存作为对象 B 的初始化器。这两个表达式都被现代编译器很好地优化了,但问题的关键不在于这样写是否有意义这样,但是当我根据placement new编写表达式时是否不违反标准。+我知道,如果我们在对象 BI 中写入一些内容,那么在使用类型 A 时将会出现未定义的行为。这是非常合乎逻辑的。
您所描述的内容与std::start_lifetime_asC++23 中的内容类似。
放置新“不保留对象表示”(正如它所说的here),这意味着读取*foo是未定义的行为,因为int对象尚未初始化。std::start_lifetime_as隐式创建具有相同对象表示形式的int对象并返回指向它的指针:
auto* foo = std::start_lifetime_as<int>(&bytes);
// A C++17 implementation
template<class T>
T* start_lifetime_as(void* p) noexcept {
// static_assert(std::is_implicit_lifetime_v<T> && std::is_trivially_copyable_v<T>);
static_assert(std::is_scalar_v<T> || std::is_array_v<T> || (std::is_class_v<T> && std::is_trivially_destructible_v<T> && std::is_trivially_copyable_v<T>));
std::byte object_representation[sizeof(T)];
std::memcpy(object_representation, p, sizeof(T));
void* result = ::new (p) std::byte[sizeof(T)];
std::memcpy(result, object_representation, sizeof(T));
return std::launder(reinterpret_cast<T*>(result));
}
Run Code Online (Sandbox Code Playgroud)
C++17 实现遵循P2590R2第 1.2 节中描述的过程。你甚至可以逃脱:
template<class T>
T* start_lifetime_as(void* p) noexcept {
std::memmove(p, p, sizeof(T)); // Compiler might be able to optimize this better than two memcpys
// return pointer to the implicitly created object
return std::launder(reinterpret_cast<T*>(p));
}
Run Code Online (Sandbox Code Playgroud)