![](/img/trans.png)
[英]Why (if that is the case) does the standard say that copying uninitialized memory with memcpy is UB?
[英]Why does constinit allow UB?
假设我像这样初始化变量:
#include <cstdint>
constexpr uint16_t a = 65535;
constinit int64_t b = a * a; // warning: integer overflow in expression of type 'int' results in '-131071' [-Woverflow]
constexpr int64_t c = a * a; // error: overflow in constant expression [-fpermissive]
由于整数溢出, b
和c
都会产生未定义的行为。
使用constinit
变量被常量初始化。 这不能保证UB。
使用constexpr
变量用常量表达式初始化。 常量表达式保证没有任何 UB。 所以这里有符号整数溢出出现错误。 但该变量也自动为 const。
那么如何最好地使用常量表达式初始化非常量变量?
我必须写吗
constexpr int64_t t = a * a; // error: overflow in constant expression [-fpermissive]
constinit int64_t b = t;
或者
constinit int64_t b = []()consteval{ return a * a; }(); // error: overflow in constant expression
每次?
这与CWG 问题 2543有关。
就目前而言,因为允许编译器在可能的情况下用静态初始化替换任何动态初始化,并且因为constinit
仅被指定为强制“无动态初始化”,所以它可能仍然允许不是常量表达式的初始化器(可能依赖于关于链接问题中讨论的解释)。 因此, constinit
反映了在运行时是否真的会进行初始化(这与避免动态初始化顺序问题有关)。 它不一定反映初始化器是否为常量表达式。
正如问题描述中所述,这实际上并不是真正可实现的,因为动态/静态初始化选择在编译过程中太晚了,无法始终使constinit
正确反映它。
对于该问题的一种可能的解决方案,可能会更改constinit
的规范,以实际要求对变量进行常量初始化,而不仅仅是要求没有动态初始化。 如果这是采用的解决方案,那么您初始化b
的第一个示例还需要编译器诊断 UB,并且所有其他解决方案都将过时。
不过,问题描述似乎并不真正支持任何方向。
对于目前的情况(如果解决方案是在另一个方向上),您给出的解决方案的替代方案是:
template<typename T>
consteval auto force_compiletime(T&& t) {
return std::forward<T>(t);
}
或者
template<typename To, typename T>
consteval To force_compiletime2(T&& t) {
return std::forward<T>(t);
}
接着
constinit auto t = force_compiletime(static_cast<int64_t>(a * a));
或者
constinit auto t = force_compiletime2<int64_t>(a * a);
请注意,您需要在初始化程序中以这种方式包含目标类型,否则将无法诊断转换中的任何潜在 UB。 如果你不关心那个
constinit int64_t t = force_compiletime(a * a);
也可以。
从技术上讲,您的问题中使用consteval
lambda 的解决方案格式不正确,不需要诊断,因为 lambda 标记为consteval
但在调用时永远不会产生常量表达式。 但我希望任何非恶意编译器仍能诊断出这样的调用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.