一位同事向我展示了一个 C++20 程序,其中使用std::bit_cast它捕获的值虚拟创建了一个闭包对象:
#include <bit>
#include <iostream>
class A {
int v;
public:
A(int u) : v(u) {}
auto getter() const {
if ( v > 0 ) throw 0;
return [this](){ return v; };
}
};
int main() {
A x(42);
auto xgetter = std::bit_cast<decltype(x.getter())>(&x);
std::cout << xgetter();
}
Run Code Online (Sandbox Code Playgroud)
由于异常,此处main函数无法调用x.getter()。相反,它调用std::bit_cast将闭包类型作为模板参数,decltype(x.getter())并将&x为新闭包对象捕获的指针作为普通参数xgetter。然后xgetter被调用以获取 object 的值,x否则在main.
该程序被所有编译器接受,没有任何警告和打印42,演示:https : //gcc.godbolt.org/z/a479689Wa
但是程序是否按照标准格式良好,lambda …
在尝试编译时字符串操作时,我遇到了一个奇怪的现象:
#include <bit>
constexpr const char str[4] = "abc";
// error: constexpr variable 'x' must be initialized by a constant expression
constexpr auto x = std::bit_cast<int>("xyz");
// OK
constexpr auto y = std::bit_cast<int>(str);
Run Code Online (Sandbox Code Playgroud)
请参阅编译器资源管理器。
为什么允许对char[]存储在全局变量中的 a进行位转换,但不允许对字符串文字进行位转换?std::bit_cast不修改其源并且结果是一个值,因此没有修改字符串文字存储的潜力。
我想知道如何std::bit_cast以明确定义的方式使用,特别是在存在不确定位的情况下。什么时候是std::bit_cast使用定义的行为,什么时候是未定义的行为?
因此,我需要澄清cppreference 关于 std::bit_cast的措辞。我不明白下面这段话的意思:
对于不确定结果的值表示中的每一位,包含该位的最小对象具有不确定值;除非该对象是 unsigned char 或 std::byte 类型,否则行为未定义。结果不包含任何不确定的值。
当输入 ( ) 包含不确定位(明显是填充位)时,所提到的对象是什么以及它们的类型(与其他类型)From的区别是什么?std::byte
让我们写一个例子(不是实际的用例,只是说明具有不确定位的情况):
#include <bit>
#include <cstdint>
struct S40 {
std::uint8_t a = 0x51;
std::uint32_t b = 0xa353c0f1;
};
struct S3 {
std::uint8_t a : 3;
};
int main() {
S40 s40;
std::uint64_t a64 = std::bit_cast<std::uint64_t>(s40);
// (3) on tested compilers, a64 is 0xa353c0f1UUUUUU51 where U is undefined byte
S3 s3;
s3.a = 0b101;
std::uint8_t a8 = std::bit_cast<std::uint8_t>(s3);
// (4) …Run Code Online (Sandbox Code Playgroud) C++20 引入了std::bit_cast来处理相同的位,就好像它们是不同的类型一样。所以,基本上它是这样做的:
template <typename T1, typename T2>
T2 undefined_bit_cast(T1 v1) {
T1 *p1 = &v1;
T2 *p2 = (T2 *)p1; // Uh oh.
T2 v2 = *p2; // Oh no! Don't do that!
return v2;
}
Run Code Online (Sandbox Code Playgroud)
除非没有未定义的行为,允许编译器用硬盘删除方法替换此方法。
我想使用 std::bit_cast,但我坚持使用 C++11。如何正确实现我自己的custom_bit_cast,不使用未定义的行为,而不使用实际的std::bit_cast?
后续问题:是否有指针位转换?有一种安全的方法可以让 aT1 *和 aT2 *指向同一位置,并且能够从两者读取和写入,而不会出现未定义的行为?
我对绕过二进制类型转换限制的方法感兴趣,因为 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 == …