web-dev-qa-db-ja.com

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

_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は正しいと思いますが、よくわかりません。

13
florestan

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>{});
}
_

実例

1