[英]if constexpr with static_assert in lambda, which compiler is correct?
當我們想在if constexpr
使用static_assert
,我們必須使條件依賴於某個模板參數。 有趣的是,當代碼包含在 lambda 中時,gcc 和 clang 不同意。
以下代碼使用 gcc 進行編譯,但 clang 會觸發斷言,即使if constexpr
不能為真。
#include <utility>
template<typename T> constexpr std::false_type False;
template<typename T>
void foo() {
auto f = [](auto x) {
constexpr int val = decltype(x)::value;
if constexpr(val < 0) {
static_assert(False<T>, "AAA");
}
};
f(std::integral_constant<int, 1>{});
}
int main() {
foo<int>();
}
通過將False<T>
替換為False<decltype(x)>
可以輕松修復它。
所以問題是:哪個編譯器是正確的? 我認為 gcc 是正確的,因為static_assert
的條件取決於T
,但我不確定。
這里通常的規則是[temp.res]/8 :
程序格式錯誤,無需診斷,如果: 不能為模板或 constexpr 的子語句生成有效的特化 模板中的 if 語句且模板未實例化
實例化foo<T>
,您擁有的static_assert
不再依賴。 它變為static_assert(false)
- 對於泛型 lambda f
的調用運算符的所有可能實例化。 這是格式錯誤的,不需要診斷。 Clang 診斷,gcc 沒有。 兩者都是正確的。
請注意,這里的static_assert
被丟棄並不重要。
通過將
False<T>
替換為False<decltype(x)>
可以輕松修復它。
這使static_assert
依賴於泛型 lambda,現在我們進入一種狀態,假設可能存在有效的特化,因此我們不再是格式錯誤的,ndr。
來自[stmt.if]/2 (強調我的)
如果 if 語句的形式為 if constexpr,則條件的值應為上下文轉換的 bool 類型常量表達式; 這種形式稱為 constexpr if 語句。 如果轉換條件的值為假,則第一個子語句是丟棄的語句,否則第二個子語句(如果存在)是丟棄的語句。 在封閉模板化實體 ([temp.pre]) 的實例化期間,如果條件在實例化后不依賴於值,則不會實例化丟棄的子語句(如果有)。
讀到人們會認為靜態斷言會被刪除,但事實並非如此。
靜態斷言在模板的第一階段被觸發,因為編譯器知道它總是假的。
來自[temp.res]/8 (強調我的)
可以在任何實例化之前檢查模板的有效性。 [注意:知道哪些名稱是類型名稱允許以這種方式檢查每個模板的語法。 —尾注] 程序格式錯誤,無需診斷,如果:
- (8.1) 不能為模板或模板內的 constexpr if 語句的子語句生成有效的特化,並且模板沒有被實例化,或者
[...]
是的,您的False<T>
取決於T
。 問題在於泛型 lambda 本身就是一個模板,並且False<T>
不依賴於 lambda 的任何模板參數。
對於一個T
的是False<T>
是假的,靜態斷言永遠是假的,無論哪個模板參數發送到拉姆達。
編譯器可以看到,對於模板operator()
任何實例化,靜態斷言將始終為當前 T 觸發。因此編譯器錯誤。
對此的解決方案是依賴於x
:
template<typename T>
void foo() {
auto f = [](auto x) {
if constexpr(x < 0) {
static_assert(False<decltype(x)>, "AAA");
}
};
f(std::integral_constant<int, 1>{});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.