C++17之std::byte

30 篇文章 17 订阅

程序在内存中保存数据。在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操作

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;
}

结果如下:

 

 

 

 

 


 

 

 

 

 

 

在 C# 中调用 C++ DLL 函数的时候,如果函数的参数是 std::string 类型,需要进行一些特殊的处理。因为在 C++ 中,std::string 类型实际上是一个类,而在 C# 中没有对应的类型。 一种解决方案是,将 C++ 函数参数中的 std::string 类型改为 char* 类型,并且增加参数来指定字符串的长度。在 C# 中,可以使用 Marshal 类的各种方法来将字符串转换为 char* 类型,并将字符串的长度传递给 C++ 函数。 下面是一个示例代码,演示了如何在 C# 中调用一个 C++ DLL 函数,该函数的参数类型为 std::string: C++ DLL 函数的代码: ```c++ #include <string> #include <iostream> // 定义一个使用 std::string 作为参数的函数 void printString(std::string str) { std::cout << str << std::endl; } ``` 在 C# 中调用该函数的代码: ```c# using System; using System.Runtime.InteropServices; class Program { // 声明 C++ DLL 函数 [DllImport("MyCppLib.dll", CallingConvention = CallingConvention.Cdecl)] static extern void printString([MarshalAs(UnmanagedType.LPStr)] string str, int length); static void Main(string[] args) { // 要传递给 C++ DLL 函数的字符串 string myString = "Hello, world!"; // 将字符串转换为 char* 类型,并获取字符串的长度 byte[] strBytes = System.Text.Encoding.ASCII.GetBytes(myString); int strLength = strBytes.Length; // 调用 C++ DLL 函数 printString(myString, strLength); } } ``` 在 C# 中,使用 [MarshalAs(UnmanagedType.LPStr)] 特性将 C++ 函数参数中的 std::string 类型转换为 char* 类型,使用 System.Text.Encoding.ASCII.GetBytes() 方法将字符串转换为 byte[] 类型,并使用该数组的长度作为字符串的长度参数传递给 C++ 函数。 希望这个示例代码对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值