簡體   English   中英

為什么 constinit 允許 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]

由於整數溢出, bc都會產生未定義的行為。

使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM