随着 std::byte 标准化,我们什么时候使用 void* 什么时候使用 byte*?

With std::byte standardized, when do we use a void* and when a byte*?

C++17 将包括 std::byte,一种用于一个原子可寻址内存单元的类型,在典型计算机上具有 8 位。

在此标准化之前,在指向 "raw" 内存时已经存在一些困境 - 在一方面使用 char*/unsigned char* 还是在另一方面使用 void *其他。现在,首选 void * 的原因之一已被删除 - std::bytechar 的含义不同;这是关于原始内存,而不是字符。

所以,我的问题是:在 std::byte 的日子里,关于什么时候比 void * 更喜欢它以及什么时候相反,什么是好的经验法则?


当然,当您处理旧代码或 C 代码时,您会受到它所接受内容的限制;我主要指的是新代码,您可以在其中选择所有类型。

首先,当您必须使用 C 库函数或一般来说使用任何其他 extern "C" 兼容函数时,void * 仍然有意义。

下一个 std::byte 数组仍然允许单独访问它的任何元素。换句话说,这是合法的:

std::byte *arr = ...;
arr[i] = std::byte{0x2a};

如果您希望能够允许低级别访问,例如,如果您想手动复制数组的全部或部分,这是有意义的。

另一方面,void * 实际上是一个 opaque 指针,从某种意义上说,您必须将它转换为(到 charbyte) 在能够访问其各个元素之前。

所以我的意见是,只要您希望能够寻址数组的元素或移动指针,就应该使用 std::byte,并且 void * 表示不透明仍然有意义只会作为一个整体通过(很难实际处理 void *)的区域。

但是 void * 的实际用例在现代 C++ 中应该变得越来越不常见,至少在高层是这样,因为那些不透明区域通常应该隐藏在更高层 类 中,并带有方法来处理它们。所以恕我直言 void * 最终应该限于 C(和旧的 C++ 版本)兼容性和低级代码(例如分配代码)。

std::byte的动机是什么?

引用自 original paper;

Many programs require byte-oriented access to memory. Today, such programs must use either the char, signed char, or unsigned char types for this purpose. However, these types perform a “triple duty”. Not only are they used for byte addressing, but also as arithmetic types, and as character types. This multiplicity of roles opens the door for programmer error - such as accidentally performing arithmetic on memory that should be treated as a byte value - and confusion for both programmers and tools.

本质上,std::byte 是 "replace" 在需要将原始内存作为字节处理时使用 char 类型,断言这是安全的按值、按引用、指针和在容器中使用时适用。

std::byte does not have the same connotations as a char; it's about raw memory, not characters

正确,因此在处理内存中的字节(如字节数组)时,std::byte 应该优先于 char 类型。立即想到一些较低级别的协议数据操作。

What is a good rule of thumb, for the days of std::byte, regarding when to prefer it over void * and when it's the other way around?

我认为类似的指南现在和以前一样适用。在处理需要字节寻址能力的原始内存块时,char * 等比 void * 更受欢迎,我认为现在适用相同的逻辑,但更喜欢 byte * 而不是 char *char * 更适合字符序列。

如果希望不透明地传递指针,void * 可能仍然最适合这个问题。 void * 本质上意味着 "point to anything",但是任何东西仍然是东西,我们只是还没有说什么。

此外,类型 uintptr_t(和 intptr_t)可能会作为备选方案考虑在内,当然这取决于所需的应用程序。

... I mostly mean new code where you get to choose all the types.

除了兼容性(您无法选择类型)之外,新代码通常对 void * 的使用非常有限。如果您需要基于字节的处理,请使用 byte *.

(这是一个潜在的经验法则,它来自我的脑海,没有任何人宽恕。)

经验法则:何时使用哪种指针?

  • 对文本字符序列使用 char *,而不是其他任何内容。
  • 在类型擦除场景中使用void *,即当指向的数据被键入时,但由于某种原因不能使用类型指针或它无法确定是否打字
  • 使用 byte * 作为原始内存,没有迹象表明它保存任何类型化的数据。

上述情况的例外情况:

  • 旧代码时也使用void */unsigned char */char *;或者当非 C++ 代码强制您使用 byte * 时。但是在执行此操作时,您仍然可以使用基于 byte * 的接口来包装此类使用,从而不会将这种情况暴露给其余的 C++ 代码。

例子

void * my_custom_malloc(size_t size) - 错误
byte * my_custom_malloc(size_t size) -

struct buffer_t { byte* data; size_t length; my_type_t data_type; } - 错误
struct buffer_t { void* data; size_t length; my_type_t data_type; } -

std::byte 不仅仅是关于 "raw memory",它是字节可寻址的原始内存 ,并为其定义了按位运算

您不应该使用 std::byte 来盲目地替换 void*void* 保留其用途。 void*对处理代码的意思是“这是一个数据块,但我不知道这个数据是什么,我也不知道如何操作在上面.

当您需要内存块的字节地址时使用std::byte并且只定义了按位运算来对该数据进行操作。

std::byte 没有 定义了常规的基本数学运算,例如 operator+operator-operator*。没错,下面的代码是非法的:

std::byte a{0b11},b{0b11000};
std::byte c = a+b; // fails, operator+ not defined for std::byte

换句话说,当不是处理代码业务内容时使用void*

std::byte 示例

就像我上面说的,你在 std::byte 上所能做的就是像 |&~ 这样的按位运算。下面是一个使用 std::byte 的例子,注意你需要一个 C++17 编译器来编译这个例子,有一个 here 但你必须从下拉列表中选择 select C++17在右上角

#include <iostream>
#include <cstddef>
#include <bitset>

using namespace std;

void print(const byte& b)
{
  bitset<8> p( to_integer<int>( b ) );
  cout << p << endl;
}

int main()
{
  byte a{0b11},b{0b11000};
  byte c=a|b;
  //byte d = a+b; // fails
  print(c);
  return 0;
}