为什么禁止非const引用位域?

Kno*_*abe 10 c++ bit-fields c++11

C++ 11中的第9.6/3节非常清楚:"非const引用不应绑定到位字段." 这项禁令背后的动机是什么?

我知道不可能将引用直接绑定到位域.但如果我宣布这样的话,

struct IPv4Header {
  std::uint32_t version:4,         // assumes the IPv4 Wikipedia entry is correct
                IHL:4,
                DSCP:6,
                ECN:2,
                totalLength:16;
};
Run Code Online (Sandbox Code Playgroud)

为什么我不能这样说呢?

IPv4Header h;

auto& ecn = h.ECN;
Run Code Online (Sandbox Code Playgroud)

我希望底层代码实际绑定到std::uint32_t包含我感兴趣的位的整个代码,并且我希望读取和写入操作生成代码以进行适当的屏蔽.结果可能是大而慢,但在我看来它应该工作.这与标准表示对const位域的引用有效的方式一致(同样来自9.6/3):

如果类型为const T&的引用的初始值设定项是引用位字段的左值,则引用绑定到初始化的临时值以保存位字段的值; 引用不直接绑定到位字段.

这表明写入位域是问题,但我不知道它是什么.我认为必要的屏蔽可能会在多线程代码中引入竞争,但是,根据1.7/3,非零宽度的相邻位域被认为是用于多线程的单个对象.在上面的示例中,IPv4Header对象中的所有位域都将被视为单个对象,因此根据定义,在读取其他字段时尝试修改字段的多线程代码已经很有效.

我显然遗漏了一些东西.它是什么?

Jon*_*rdy 10

您不能const对位域进行非引用,原因与您无法获取其地址的原因相同&:它的实际地址不一定是对齐的char,这在定义上是C++抽象机器中最小的可寻址内存单元.您可以const参考它,因为编译器可以自由复制该值,因为它不会被突变.

考虑单独编译的问题.const uint32_t&需要使用相同代码对任何操作进行操作的函数const uint32_t&.如果普通值和位域值需要不同的写行为,则该类型不会编码足够的信息,以使函数在两者上都能正常工作.

  • 最后一点是错误的。const 引用不暗示值是否发生变异。它仅通过 *that* 引用防止突变。 (2认同)

AnT*_*AnT 9

由于指针不能指向位字段,因此非const引用不能绑定到位字段.

虽然没有指定引用是否占用存储,但很明显,在非平凡的情况下,它们被伪装成指针实现,并且这种引用的实现是由该语言的作者"预期"实现的.就像指针一样,引用必须指向可寻址的存储单元.将非const引用绑定到不可寻址的存储单元是不可能的.由于非const引用需要直接绑定,因此非const引用不能绑定到位字段.

产生指向位字段的指针/引用的唯一方法是实现某种"超级指针",除了存储器中的实际地址外,它还包含某种位偏移和位宽信息,为了告诉写代码要修改哪些位.请注意,此附加信息必须存在于所有数据指针类型中,因为C++中没有"指向/引用位域"的类型.这基本上等同于实现更高级别的存储寻址模型,与底层OS /硬件平台提供的寻址模型完全脱节.C++语言从不打算从纯粹的效率考虑中要求从底层平台那种抽象.

一种可行的方法是引入一个单独的指针/引用类别,例如"指向/指向位域的引用",它将具有比普通数据指针/引用更复杂的内部结构.这些类型可以从普通数据指针/引用类型转换,但不能相反.但它似乎不值得.

在实际情况中,当我必须处理打包成比特和比特序列的数据时,我通常更喜欢手动实现比特字段并避免语言级别的比特字段.位字段的名称是编译时实体,不可能选择任何类型的运行时.当需要运行时选择时,更好的方法是声明普通uint32_t数据字段并手动管理其中的各个位和位组.这种手动"位字段"的运行时选择很容易通过掩码和移位来实现(两者都可以是运行时值).基本上,这接近于上述"超级指针"的手动实现.