我知道在编译时复制任意的内存块并不总是可能的,但是既然我们得到了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
的变通方法。
2条答案
按热度按时间goucqfw61#
运行时代码中的C对象模型通常被松散地对待。它有相当严格的规则,但有一堆后门要么被允许,要么被声明为UB。后者意味着你仍然可以编写代码来做这件事,但C对代码的行为没有任何保证。
在常数评估范围内(即:编译时执行代码),但事实并非如此。
constexpr
的限制是专门为了允许对象模型成为你必须遵循的真实的事物,没有可行的后门。甚至它偶尔允许的后门也被明确要求是病态的,并产生编译错误,而不是沉默的UB。基本上在运行时,你可以把内存当作字节存储,而在编译时,你 * 不能 *;你是不被允许的,即使在C++20中加入了动态分配的代码,你也不能玩很多你通常会玩的游戏。
memcpy
以字节为单位处理存储,来回复制它们而不知道它们的含义。bit_cast
* 知道 * 源对象和目标对象,除非源对象和目标对象适合bit_cast
ing(即:可平凡复制)。bit_cast
在这两个对象的 content 上也有very specific restrictions,如果你想让它在编译时工作的话。特别是,你不能使用bit_cast
指针或任何对象 * 包含 * 任何类型的指针或引用。这是因为编译时的指针不仅仅是地址,为了捕获UB,编译时指针必须知道它所指向的对象的真正动态类型,所以编译时不允许只转换地址的指针转换。
1l5u6lss2#
这更多的是一个评论,而不是一个答案,因为我只是引用P0202R0中所写的内容:将Constexpr修饰符添加到和Header中的函数,但我在这里写它,因为它不适合注解:
std::memmove
和std::memcpy
必须有常量表达式加法**std::memmove
和std::memcpy
接受void*
和const void*
参数。这使得它们不可能在纯C++中实现为constexpr
,因为常量表达式不能根据[expr. const]计算从类型cv void *
到指向对象的指针类型的转换。然而,这些函数不仅流行,而且在整个标准库中广泛使用,以获得更好的性能。不使它们成为constexpr将迫使标准库开发人员无论如何都要为它们提供编译器内部函数。这是必须完成的艰难一步。
[expr.const]
的相关章节:[...]表达式
e
是一个 * 核心常量表达式 *,除非e
的求值遵循抽象机规则(6.8.1),将求值以下表达式之一:[...]
(2.13)-从类型
cv void*
到 * 指向对象的指针 * 类型的转换;