ラムダでstatic_assertを指定したconstexprの場合、どのコンパイラが正しいですか?


13

static_assertでa if constexprを使用する場合は、条件をいくつかのテンプレートパラメータに依存させる必要があります。興味深いことに、コードがラムダでラップされている場合、gccとclangは一致しません。

次のコードはgccでコンパイルされますが、clang 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はに依存しているので、gccは正しいと思いますTが、よくわかりません。


これは一種の同じ質問をしますが、反対方向から来ます:stackoverflow.com/questions/59393908/…
NathanOliver

1
@mfnx 再現できません。例を共有できますか?
NathanOliver

2
私はどちらも正しいと思います(正しく形成されていないNDR):ラムダの内側static_assert(False<int>, "AAA");と同等static_assert(false, "AAA");です。
Jarod42

2
@mfnx定数の値を変更しました。定数がf(std::integral_constant<int, 1>{});Wandbox であるOPの例を使用しても、アサートはトリガーされません:wandbox.org/permlink/UFYAmYwtt1ptsndr
NathanOliver

1
@NathanOliverはい、あなたは正しいです、ノイズのため申し訳ありません。定数> = 0の場合はconstexpr内のコードを破棄する必要があるため、gccは正しいようです。
mfnx

回答:


1

[stmt.if] / 2(強調鉱山)

ifステートメントがif constexprの形式である場合、条件の値はbool型のコンテキスト変換された定数式でなければなりません。この形式はconstexpr ifステートメントと呼ばれます。変換された条件の値がfalseの場合、最初のサブステートメントは破棄されたステートメントです。それ以外の場合、2番目のサブステートメント(存在する場合)は破棄されたステートメントです。囲んでいるテンプレート化されたエンティティ([temp.pre])のインスタンス化中に、インスタンス化後に条件が値に依存しない場合、破棄されたサブステートメント(存在する場合)はインスタンス化されません。

静的アサートは削除されると思いますが、そうではありません。

コンパイラーは常にfalseであることをコンパイラーが認識しているため、静的アサートはテンプレートの最初のフェーズでトリガーされます。

[temp.res] / 8(強調鉱山)

テンプレートの有効性は、インスタンス化の前にチェックできます。[ 注:どの名前がタイプ名であるかを知ることで、すべてのテンプレートの構文をこの方法でチェックできます。— 終了注 ]次の場合、プログラムの形式が正しくなく、診断は必要ありません。

  • (8.1)テンプレートまたはテンプレート内のステートメントがインスタンス化されていない場合、テンプレートまたはconstexprのサブステートメントに対して有効な特殊化を生成できない、または

[...]

はい、確かに、あなたFalse<T>はに依存していTます。問題は、一般的なラムダ自体がテンプレートであり、False<T>、ラムダのテンプレートパラメータに依存しないことです。

そのためTFalse<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>{});
}

実例


13

ここでの通常のルールは[temp.res] / 8です。

次の場合、プログラムの形式が正しくなく、診断は必要ありません。テンプレートまたはテンプレート内のconstexpr ifステートメントの有効な特殊化を生成できず、テンプレートがインスタンス化されていない場合

をインスタンス化するfoo<T>と、static_assert依存しなくなります。これstatic_assert(false)は、ジェネリックラムダの呼び出し演算子のすべての可能なインスタンス化になりますf。これは形式が正しくなく、診断は必要ありません。Clangは診断しますが、gccは診断しません。どちらも正しいです。

static_assertここ破棄されても問題ないことに注意してください。

それは簡単に置き換えることによって固定することが可能False<T>False<decltype(x)>

これによりstatic_assert、ジェネリックラムダ内で依存関係が維持され、仮想的に有効な特殊化が存在する可能性がある状態になり、不正な形式のndrではなくなりました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.