std::memcpy即使在C++20中也不是constexpr的原因是什么?

00jrzges  于 2022-12-24  发布在  其他
关注(0)|答案(2)|浏览(217)

我知道在编译时复制任意的内存块并不总是可能的,但是既然我们得到了constexpr容器,虚方法和算法,为什么不也用memcpy呢?它也是一种算法。
此外,

  • C++20 std::bit_cast看起来很像std::memcpy的变通方法,但它是constexpr
  • 使用迭代器的std::copy在C++20中被标记为constexpr,所以复制类型是可能的。

它的用法是复制或者只是“重新解释”constexpr函数中的变量/数组,前者不是std::bit_cast AFAIK所能解决的,特别是question和我的答案希望使用它。

  • 为什么std::bit_cast可以是constexpr而std::memcpy不能是constexpr,有什么特别的原因吗?
  • 它是否与使用void指针而不是类型化引用的memcpy有关?
  • 实际上不用复制任何东西?
  • C向后兼容性?
  • 也许是因为没有对“指向constexpr内存的指针”的支持,但是这同样适用于std::bit_cast中的reference参数和std::copy中的迭代器。

C++20 bit_cast vs reinterpret_cast的相关答案简要引用自某处:
此外,目前还不可能实现constexpr位转换函数,因为memcpy本身不是constexpr。将建议的函数标记为constexpr并不要求或阻止memcpy成为constexpr,但需要编译器支持。这使得实现可以自由使用自己的内部解决方案(例如LLVM有一个位转换操作码)。
但它没有详细说明不要让它也成为constexpr。
请注意,我不问为什么std::bit_cast存在。我喜欢它,它提供了一个明确的意图,而不是std::memcpy的变通方法。

goucqfw6

goucqfw61#

运行时代码中的C对象模型通常被松散地对待。它有相当严格的规则,但有一堆后门要么被允许,要么被声明为UB。后者意味着你仍然可以编写代码来做这件事,但C对代码的行为没有任何保证。
在常数评估范围内(即:编译时执行代码),但事实并非如此。constexpr的限制是专门为了允许对象模型成为你必须遵循的真实的事物,没有可行的后门。甚至它偶尔允许的后门也被明确要求是病态的,并产生编译错误,而不是沉默的UB。
基本上在运行时,你可以把内存当作字节存储,而在编译时,你 * 不能 *;你是不被允许的,即使在C++20中加入了动态分配的代码,你也不能玩很多你通常会玩的游戏。
memcpy以字节为单位处理存储,来回复制它们而不知道它们的含义。bit_cast * 知道 * 源对象和目标对象,除非源对象和目标对象适合bit_cast ing(即:可平凡复制)。
bit_cast在这两个对象的 content 上也有very specific restrictions,如果你想让它在编译时工作的话。特别是,你不能使用bit_cast指针或任何对象 * 包含 * 任何类型的指针或引用。
这是因为编译时的指针不仅仅是地址,为了捕获UB,编译时指针必须知道它所指向的对象的真正动态类型,所以编译时不允许只转换地址的指针转换。

1l5u6lss

1l5u6lss2#

这更多的是一个评论,而不是一个答案,因为我只是引用P0202R0中所写的内容:将Constexpr修饰符添加到和Header中的函数,但我在这里写它,因为它不适合注解:

    • B. std::memmovestd::memcpy必须有常量表达式加法**

std::memmovestd::memcpy接受void*const void*参数。这使得它们不可能在纯C++中实现为constexpr,因为常量表达式不能根据[expr. const]计算从类型cv void *到指向对象的指针类型的转换。
然而,这些函数不仅流行,而且在整个标准库中广泛使用,以获得更好的性能。不使它们成为constexpr将迫使标准库开发人员无论如何都要为它们提供编译器内部函数。这是必须完成的艰难一步。
[expr.const]的相关章节:

    • 8.6常量表达式[expr. const]**

[...]表达式e是一个 * 核心常量表达式 *,除非e的求值遵循抽象机规则(6.8.1),将求值以下表达式之一:
[...]
(2.13)-从类型cv void*到 * 指向对象的指针 * 类型的转换;

相关问题