【问题标题】:What is the reason for std::memcpy not being constexpr even in C++20?即使在 C++20 中 std::memcpy 也不是 constexpr 的原因是什么?
【发布时间】:2021-01-10 11:25:40
【问题描述】:

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

此外,

  • C++20 std::bit_cast 看起来很像std::memcpy workaround reinterpret_cast 但它是constexpr
  • 对于 C++20,使用迭代器的 std::copy 被标记为 constexpr,因此对于类型来说,复制是可能的。

用法是复制或只是“重新解释”constexpr 函数中的变量/数组,std::bit_cast AFAIK 无法解决前者。特别是question和我的回答想用它。

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

C++20 bit_cast vs reinterpret_cast 的相关答案从某处简要引用:

此外,目前无法实现 constexpr 位转换函数,因为 memcpy 本身不是 constexpr。标记 提议的函数作为 constexpr 不需要或阻止 memcpy 成为 constexpr,但需要编译器支持。这离开 实现免费使用自己的内部解决方案(例如 LLVM 有 位广播操作码)。

但它并没有详细说明不使其成为 constexpr。

注意,我没有问为什么std::bit_cast 存在。我喜欢它,它提供了一个明确的意图,而不是 std::memcpy 解决方法。

【问题讨论】:

    标签: c++ constexpr c++20


    【解决方案1】:

    运行时代码中的 C++ 对象模型通常处理得有些松散。它有相当严格的规则,但是有一堆后门被允许或声明为 UB。后者意味着您仍然可以编写代码来执行此操作,但 C++ 不保证该代码的行为。

    在持续评估(又名:代码的编译时执行)中,不是的情况。对constexpr 的限制专门用于使对象模型成为您必须遵循的真实事物,没有可行的后门。甚至它偶尔允许的那些也被明确要求格式错误并产生编译错误,而不是沉默的 UB。

    基本上在运行时,您可以将内存视为存储字节。在编译时,你不能;你不被允许。即使在 C++20 中添加了 constexpr 代码中的动态分配,你也不会玩很多你通常会玩这种东西的游戏。

    memcpy 处理字节存储,来回​​复制它们而不知道它们的含义。 bit_cast 知道源对象和目标对象,除非源对象和目标对象适合bit_casting(即:普通可复制),否则它不允许您这样做。 p>

    bit_cast 在这两个对象的 content 上也有 very specific restrictions,如果您希望它在编译时工作。特别是,您不能bit_cast 指针或任何对象包含 任何类型的指针。或参考文献。

    这是因为编译时的指针不仅仅是地址。为了捕捉 UB,编译时指针必须知道它指向的对象的真正动态类型。因此,在编译时不允许仅转换地址的指针转换。

    【讨论】:

    • 感谢您的回答,很抱歉没有活动。你能告诉std::copy是constexpr吗?是否适用与bitcast 相同的限制?那么它如何工作,因为每个迭代器都应该至少包含对容器的引用。
    • @Quimby:有什么要解决的?不要将实现与行为混淆。 std::copy 复制对象;这就是它的行为。 std::copyconstexpr;这也是它的行为。 该函数内发生什么以创建该行为与您无关,最终与该函数的行为方式无关。
    • 好吧,很公平,我只是感兴趣,因为std::copy 看起来真的像打字版std::memcpy。但我猜没有什么能阻止我研究一些实现。
    【解决方案2】:

    这更像是一个评论而不是一个答案,因为我只是引用P0202R0: Add Constexpr Modifiers to Functions in and Headers 中写的内容,但我在这里写它不适合 cmets:

    B. std::memmovestd::memcpy 必须有 constexpr 添加
    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* 类型到pointer-to-object 类型的转换;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-01
      • 2018-02-11
      • 2016-03-14
      • 2020-08-06
      • 2020-10-30
      • 1970-01-01
      相关资源
      最近更新 更多