位字段和访问说明符



是否允许在c++中为位字段指定不同的访问说明符?例如:

struct S1 {
unsigned f1    : 2;
unsigned f2    : 2;
private: // 1
unsigned f3    : 2;
public: // 2
unsigned f4    : 2;
};

因此,问题是是否允许在第1行和第2行中指定访问说明符?

简短回答

是的。

答案很长

另一方面,违反严格的混叠规则则不然。这是一个常见的问题,人们只是假设这一点和其他一些C++语言功能,然后盲目地在没有的地方寻找问题。

这个";奇怪的";你在评论中提到的bug闻起来很像你试图重新解释,将std::byte转换到你的结构中,尽管"显然";可能是未定义的行为。这里唯一奇怪的是,在处理低级别数据存储时存在大量的懒惰,这一点在写这个问题时付出了多少努力也非常明显。

好吧,不管怎样,你为什么要尝试重新解释比特字段结构的强制转换字节,即使这不是你要做的?因为它更快?更糟糕的是,你的程序甚至从来没有正确编译过,因为你的编译器认为你不会这么做?未定义的行为是未定义的,但后果显然是。

您必须记住,位字段是而不是可移植的,主要是为了方便而非必要。如果你想要速度,你每次都会放弃方便,例如,假设你正在做的事情会更快而不分析你的代码。如果你对你的代码进行了剖析;复数";您可以通过网络发送或独立写入磁盘平台的原始字节之间的转换确实是一个瓶颈,您将从位字段回落到手动排列字节,这就是我们程序员生活的现实。

同时,像你的问题中这样的构造是完全没有价值的,所以即使不可能有私有比特字段,这也不是一个问题,因为原始数据(而不是POD!(应该像原始数据一样处理,如果你想要一些特定的抽象行为,你可以将这些原始数据作为私有成员放入一个真实的类中,所有字段都是封闭类的公共字段,然后从那里开始工作。

但我离题了,在阅读这篇咆哮式的解释时,每个人都痛苦地渴望得到以下内容——一个清晰、干净、明显、正确、独立于平台且理智的真实例子:

#include <cstddef>
#include <iostream>
struct packed_struct {
unsigned a: 2;
unsigned b: 2;
unsigned c: 1;
unsigned d: 2;
unsigned e: 1;
packed_struct() noexcept
: a{},
b{},
c{},
d{},
e{}
{}
// no, this struct is NOT supposed to be a POD, the POD is right here, std::byte, the
// plainest and oldest data you will ever see
explicit packed_struct(std::byte raw) noexcept
// you only need to get this right once
// https://en.cppreference.com/w/cpp/language/operator_precedence
: a(static_cast<unsigned>(raw) >> 0 & 0b11),
d(static_cast<unsigned>(raw) >> 2 & 0b11),
b(static_cast<unsigned>(raw) >> 4 & 0b11),
e(static_cast<unsigned>(raw) >> 6 &  0b1),
c(static_cast<unsigned>(raw) >> 7 &  0b1)
// what is this arbitrary order you ask? well... not all data we get is
// our own, so it is not always ordered how we might want it to be;
// in our project we have this stupid rule to order same size fields
// alphabetically, and while noone likes it, noone wants to challenge
// whoever made it up, so this will have to do
// think this is unrealistic? Think again
{}
explicit operator std::byte() const noexcept
{
std::byte raw{};
raw |= static_cast<std::byte>(a << 0);
raw |= static_cast<std::byte>(d << 2);
raw |= static_cast<std::byte>(b << 4);
raw |= static_cast<std::byte>(e << 6);
raw |= static_cast<std::byte>(c << 7);
return raw;
}
};
// well, where's the benefit of this if we did it all manually? the answer is
// we did only what we always have to, while compiler did a lot of free and a
// lot more annoying work for us
int main() {
// there is a clear line between human understandable data
packed_struct s;
s.a = 1;
s.b = 3;
s.c = 0;
s.d = 2;
s.e = 1;
// and an unintelligible block of bits that noone needs to know the layout of
// but we do know the layout, we wrote the conversion functions, didn't we?
// no, I in fact do not know which bit is "3rd" or "7th" or even "29th", 
// and guess what, I don't care either
std::byte b = static_cast<std::byte>(s);
// but can still be effortlessly stored in a file and even moved across
// systems, ignoring any details including processor architecture...
// and yet, they can all be converted back and forth effortlessly
packed_struct r(~b); // you can even mess up the bits in a POD
// and still get exactly what you expect, no assumptions, ever
std::cout << "r.a = " << r.a << 'n'  // 2
<< "r.b = " << r.b << 'n'  // 0
<< "r.c = " << r.c << 'n'  // 1
<< "r.d = " << r.d << 'n'  // 1
<< "r.e = " << r.e << 'n'; // 0
}

std::byte相比,更大的类型也可以做到这一点,如果你理解了这背后的基本思想,你就不会有问题地弄清楚如何使用完全相同的逻辑来编码和解码unsigned shortlong int。。。即使在每个字节访问都有随机化字节序的系统上,只要有编译器支持,它也能正确工作

既然我已经写了这么多,我还可以举一个更大类型的例子:

unsigned short b{};
std::array<std::byte, 2> bytes;
// <read 2 bytes from somewhere like using fread and a.data()>

这里是唯一重要的部分——哪个字节去哪里?不,这与你的CPU体系结构完全无关,你不应该把这些东西混为一谈。我可以先写后写,也可以先写,不管什么机器运行这个,都会一样,只要你是一致的,我不需要告诉你要一致。

b |= static_cast<unsigned short>(bytes[0] << 8);
b |= static_cast<unsigned short>(bytes[1] << 0);
packed_struct_2bytes s2(b); // TODO: implement this type

这里需要说明的是,您定义的格式是为程序员而不是机器设计的。是的,它是"从第3字节的第6位开始的2位";,但你必须明白这到底意味着什么。通过将4字节整数类型视为{1, 3, 4, 2}(不要与C数组语法混淆(,您的CPU可以向后计数字节,甚至可以在末尾之间来回计数,其中a[2] == 2。您不需要知道这一点,就像使用位字段功能时不需要知道位是如何打包的一样。我们发明了编程语言,因为处理真实的机器很难。不利的一面是;知道";机器的东西在边缘情况下对你没有帮助,但它对你来说并不重要,当它重要的时候,你会向每个人解释事情,而不是问问题。

一旦你了解了这些琐碎的事情,你所有的"奇怪的虫子";将永久消失。

毫不奇怪,如果你真的需要,你可以在这个结构中使任何字段私有,它会很好地工作。

最新更新