C++11中的9.6/3节异常清晰:“非常量引用不应绑定到位字段。”这一禁止背后的动机是什么?
我知道直接将引用绑定到位域是不可能的。但是如果我声明这样的东西,
struct IPv4Header {
std::uint32_t version:4, // assumes the IPv4 Wikipedia entry is correct
IHL:4,
DSCP:6,
ECN:2,
totalLength:16;
};
为什么我不能说
IPv4Header h;
auto& ecn = h.ECN;
我希望底层代码实际上绑定到包含我感兴趣的位的整个std::uint32_t
,我希望读写操作生成代码来执行适当的掩码。但在我看来,它应该工作。这将是一致的方式标准说,引用const
位域的工作(又从9.6/3开始):
如果类型为const T&的引用的初始化器是引用位字段的左值,则引用被绑定到临时初始化以保存位字段的值;引用不直接绑定到比特字段。
这表明写入位域是问题所在,但我不明白是什么。我考虑过必要的掩码可能会在多线程代码中引入竞争,但根据1.7/3,出于多线程的目的,相邻的非零宽度的位域被视为单个对象。在上面的示例中,IPv4Header
对象中的所有位域都将被视为单个对象,因此,根据定义,试图在阅读其他字段的同时修改一个字段的多线程代码已经是活泼的。
我肯定漏掉了什么。是什么?
2条答案
按热度按时间chhqkbe11#
非常量引用不能绑定到位域,原因与指针不能指向位域相同。
虽然没有指定引用是否占用存储空间,但很明显,在非平凡的情况下,它们被实现为伪装的指针,而这种引用的实现是语言作者“有意”的。就像指针一样,引用必须指向一个可寻址的存储单元。在普通硬件中,最小的可寻址存储单元是每字节(不是每一位)。不可能将非常量引用绑定到不可寻址的存储单元。由于非常量引用需要直接绑定一个非常量引用不能绑定到一个位域。你可以使用一个const引用,因为编译器被允许复制这个值。
产生可以指向位字段的指针/引用的唯一方式将是实现某种“超级指针”,其除了存储中的实际地址之外还将包含某种位偏移和位宽信息,以便告诉写入代码要修改哪些位。注意,该附加信息将必须存在于所有数据指针类型中。因为在C中没有“位字段指针/引用”这样的类型。这基本上等同于实现一个更高级的存储寻址模型,与底层OS/硬件平台提供的寻址模型完全分离。 C 从未打算出于纯粹的效率考虑而要求从底层平台进行这种抽象。
一个可行的方法是引入一个单独的指针/引用类别,如“位域指针/引用”,它的内部结构比普通的数据指针/引用更复杂。这种类型可以从普通的数据指针/引用类型转换而来,但不能反过来。但似乎不值得。
在实际情况中,当我必须处理打包成位和位序列的数据时,我通常更喜欢手动实现位字段,避免语言级位字段。位字段的名称是编译时实体,没有任何类型的运行时选择的可能性。当运行时选择是必要的时,一个更好的方法是声明一个普通的
uint32_t
数据字段,并手动管理其中的单个位和位组。2这种手动“位字段”的运行时选择很容易通过掩码和移位来实现基本上,这接近于上述“超级指针”的手动实现。3xiyfsfu2#
你不能使用非
const
引用一个位域,原因和你不能使用&
引用它的地址是一样的:它的实际地址不一定与char
对齐,char
是C++抽象机器中定义的最小可寻址内存单元。您可以引用const
,因为编译器可以自由地 * 复制 * 该值,因为它不会发生变化。考虑单独编译的问题。一个接受
const uint32_t&
的函数需要使用相同的代码来操作任何const uint32_t&
。如果普通值和位域值需要不同的写行为,那么类型没有编码足够的信息来使函数在两者上正确工作。