程序在内存中保存数据。在c++ 17引入了 一种std::byte类型,它确实表示内存元素的“nature”类型字节。与char或int类型的关键区别在于,它不是字符类型且非算术类型。 byte 只是位的汇集,而且只对它定义逐位运算符。即唯一支持的“计算”操作是位操作符。
1. std::byte的使用
下面的例子演示了std::byte:
#include <cstddef> // for std::byte
std::byte b1{0x3F};
std::byte b2{0b1111'0000};
std::byte b4[4] {b1, b2, std::byte{1}}; // 4 bytes (last is 0)
if (b1 == b4[0])
{
b1 <<= 1;
}
std::cout << std::to_integer<int>(b1) << '\n'; // outputs: \T{126}
这里,我们用两个不同的初值定义了两个字节。b2初始化使用c++ 14以来可用的两个特性:
- 前缀0b定义二进制文字。
- “数字分隔符”使数字文字在源代码中更具可读性(它可以放在任何两个数字文字之间)。
注意,列表初始化(使用大括号)是直接初始化std::byte对象的单个值的唯一方法。
std::byte b1{42}; // OK (as for all enums with fixed underlying type since C++17)
std::byte b2(42); // ERROR
std::byte b3 = 42; // ERROR
std::byte b4 = {42}; // ERROR
这是将std::byte实现为枚举类型的直接结果,使用新的方法可以用整数值。由于 C++17 放松的 enum class 初始化规则,能用 std::byte{n
} 转换数值 n
为 byte 值。能用 std::to_integer 转换 byte 为数值(例如生成对象的整数哈希)。
也没有隐式转换,所以你必须用显式转换的整数文字初始化字节数组:
std::byte b5[] {1}; // ERROR
std::byte b5[] {std::byte{1}}; // OK
如果不进行任何初始化,std::byte的值将为堆栈上的对象未定义:
std::byte b; // undefined value
与往常一样(除了atomics),可以强制初始化,所有的位都设置为零,并使用列表初始化:
std::byte b{}; // same as bf0g
std::to_integer<>()提供了使字节对象转换为整数值的方法(包括bool和char)。如果没有转换,输出操作符将无法编译。注意,因为std::to_integer<>是一个模板:
std::cout << b1; // ERROR
std::cout << to_integer<int>(b1); // ERROR (ADL doesn’t work here)
std::cout << std::to_integer<int>(b1); // OK
使用std::byte作为布尔值也需要这样的转换。例如:
if (b2) ... // ERROR
if (b2 != std::byte{0}) ... // OK
if (to_integer<bool>(b2)) ... // ERROR (ADL doesn’t work here)
if (std::to_integer<bool>(b2)) ... // OK
因为std::byte被定义为枚举类型,并且unsigned char作为底层类型,所以std::byte的大小总是1:
std::cout << sizeof(b); // always 1
位的数量取决于无符号字符类型的位的数量,可以通过标准的数字限制找到:
std::cout << std::numeric_limits<unsigned char>::digits; // number of bits of a std::byte
std::byte大多数时候是8bit,但是有些平台不是这样的。
2. std::byte的类型和操作
本节详细描述std::byte的类型和操作。
2.1 std::byte的类型
在头文件<cstddef>中,c++标准库定义了std::byte类型,如下所示:
namespace std
{
enum class byte : unsigned char
{
};
}
也就是说,std::byte只是一个作用域枚举类型,定义了一些补充的位操作符:
namespace std
{
...
template<typename IntType>
constexpr byte operator<< (byte b, IntType shift) noexcept;
template<typename IntType>
constexpr byte& operator<<= (byte& b, IntType shift) noexcept;
template<typename IntType>
constexpr byte operator>> (byte b, IntType shift) noexcept;
template<typename IntType>
constexpr byte& operator>>= (byte& b, IntType shift) noexcept;
constexpr byte& operator|= (byte& l, byte r) noexcept;
constexpr byte operator| (byte l, byte r) noexcept;
constexpr byte& operator&= (byte& l, byte r) noexcept;
constexpr byte operator& (byte l, byte r) noexcept;
constexpr byte& operator^= (byte& l, byte r) noexcept;
constexpr byte operator^ (byte l, byte r) noexcept;
constexpr byte operator~ (byte b) noexcept;
template<typename IntType>
constexpr IntType to_integer (byte b) noexcept;
}
2.2 std::byte操作
操作 | 说明 |
constructors | 创建std::byte对象(使用默认构造函数未定义值) |
destructor | 销毁一个byte对象(不做任何操作) |
= | 分配一个新值 |
==, !=, <, <=, >, >= | 比较byte对象 |
<<, >>, |, &, ^, ~ | 二进制位的运算 |
<<=, >>=, |=, &=, ^= | 修改二进制位的运算 |
to_integer<T>() | 将byte对象转换为整数类型T |
sizeof() | byte的大小,一般是1 |
1. 转换为整数类型
通过使用to_integer<>()可以将std::byte转换为任何基本的整数类型(bool,char,int...),例如,这对于将std::byte与数值进行比较或在条件下使用它是必要的:
if (b2) ... // ERROR
if (b2 != std::byte{0}) ... // OK
if (to_integer<bool>(b2)) ... // ERROR (ADL doesn’t work here)
if (std::to_integer<bool>(b2)) ... // OK
to_integer<>()使用从无符号字符到目标类型的静态强制转换规则。例如:
std::byte ff{0xFF};
std::cout << std::to_integer<unsigned int>(ff); // 255
std::cout << std::to_integer<int>(ff); // also 255 (no negative value)
std::cout << static_cast<int>(std::to_integer<signed char>(ff)); // -1
2. I/O与std::byte
没有为std::byte定义输入和输出操作符,因此必须将它们转换为整数值:
std::byte b;
...
std::cout << std::to_integer<int>(b); // prints value as decimal value
std::cout << std::hex << std::to_integer<int>(b); // prints value as hexadecimal value
通过使用std::bitset<>,还可以输出二进制值(位序列):
#include <bitset>
#include <limits>
using ByteBitset = std::bitset<std::numeric_limits<unsigned char>::digits>;
std::cout << ByteBitset{std::to_integer<unsigned char>(b1)};
using声明定义了一个bitset类型,其位数为std::byte,然后我们创建并输出这样一个对象,该对象初始化时使用的是字节的整数类型。
也可以用它把std::byte的二进制表示写进一个字符串:
std::string s = ByteBitset{std::to_integer<unsigned char>(b1)}.to_string();
输入也可以采用类似的方法:只需将值读入整数、字符串或位集值并进行转换。
例如,可以编写一个输入操作符,从二进制表示中读取一个字节,如下所示:
std::istream& operator>> (std::istream& strm, std::byte& b)
{
// read into a bitset:
std::bitset<std::numeric_limits<unsigned char>::digits> bs;
strm >> bs;
// without failure, convert to std::byte:
if (! std::cin.fail()) {
b = static_cast<std::byte>(bs.to_ulong()); // OK
}
return strm;
}
注意,我们必须使用static_cast<>()来转换为bitset,将位bitset换为无符号long,再转换为std::byte。列表初始化无法工作,因为转换范围缩窄:
b = std::byte{bs.to_ulong()}; // ERROR: narrowing
下面举个例子:
#include <iostream>
#include <string>
#include <cstddef>
#include <bitset>
#include <limits>
int main()
{
std::byte b1{ 0x3F };
std::byte b2{ 0b1111'0000 };
std::byte b4[4]{ b1, b2, std::byte{1} }; // 4 bytes (last is 0)
std::byte b5[]{ std::byte{1} };
std::byte b6{};
if (b2 == std::byte{ 0b1111'0000 })
{
std::cout << std::to_integer<int>(b2) << std::endl;
}
using ByteBitset = std::bitset<std::numeric_limits<unsigned char>::digits>;
std::cout << ByteBitset{ std::to_integer<unsigned char>(b1) } << std::endl;
std::string s = ByteBitset{ std::to_integer<unsigned char>(b2) }.to_string();
std::cout << s << std::endl;
return 0;
}
结果如下: