[英]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.