_static_assert
_で_if constexpr
_を使用する場合は、条件をいくつかのテンプレートパラメータに依存させる必要があります。興味深いことに、コードがラムダでラップされている場合、gccとclangは一致しません。
次のコードはgccでコンパイルされますが、_if constexpr
_がtrueでなくても、clangはアサートをトリガーします。
_#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)>
に置き換えることで簡単に修正できます。
だから問題は:どのコンパイラーが正しいのか? _static_assert
_の条件はT
に依存するため、gccは正しいと思いますが、よくわかりません。
From [stmt.if]/2 (強調は私のもの)
Ifステートメントがif constexprの形式である場合、条件の値はbool型のコンテキスト変換された定数式でなければなりません。この形式はconstexpr ifステートメントと呼ばれます。変換された条件の値がfalseの場合、最初のサブステートメントは破棄されたステートメントです。それ以外の場合、2番目のサブステートメントは、存在する場合、破棄されたステートメントです。 テンプレート化されたエンティティ([temp.pre])のインスタンス化中に、インスタンス化後に条件が値に依存しない場合、破棄されたサブステートメント(存在する場合)はインスタンス化されません。
静的アサートは削除されると思いますが、そうではありません。
コンパイラーは常にfalseであることをコンパイラーが認識しているため、静的アサートはテンプレートの最初のフェーズでトリガーされます。
から [temp.res]/8 (鉱山を強調)
テンプレートの有効性は、インスタンス化の前にチェックできます。 [注:どの名前がタイプ名であるかを知ることで、すべてのテンプレートの構文をこの方法でチェックできます。 —end note]次の場合、プログラムの形式が正しくないため、診断は必要ありません。
- (8.1)テンプレートまたはconstexprのサブステートメントに対して有効な特殊化を生成できず、テンプレート内のステートメントとテンプレートがインスタンス化されていない、または
[...]
はい、確かに、あなたの_False<T>
_はT
に依存しています。問題は、一般的なラムダ自体がテンプレートであり、_False<T>
_がラムダのテンプレートパラメータに依存しないことです。
_False<T>
_がT
である場合、どのテンプレート引数がラムダに送信されても、静的アサートは常にfalseになります。
コンパイラーは、テンプレート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>{});
}
_