assert()とstatic_assert()の間でディスパッチする方法、constexprコンテキストに依存するか?


8

C ++ 11 constexpr関数では、anなどの2番目のステートメントassert()は使用できません。A static_assert()は問題ありませんが、関数が「通常の」関数として呼び出された場合は機能しません。カンマ演算子はwrtoを助けるために来るかもしれません。assert()しかし、醜いですし、いくつかのツールは、それについての警告を吐きます。

アサーションのほかに完全に制約可能な「ゲッター」を検討してください。しかし、実行時とコンパイル時に何らかのアサーションを保持したいのですが、 'constexpr'コンテキストによってはオーバーロードすることはできません。

template<int Size>
struct Array {
  int m_vals[Size];
  constexpr const int& getElement( int idx ) const
  {
    ASSERT( idx < Size ); // a no-go for constexpr funcs in c++11
    // not possible, even in constexpr calls as being pointed out, but what I would like:
    static_assert( idx < Size, "out-of-bounds" );
    return m_vals[idx];
  }
};

サイドコンディション:C ++ 11、ヒープ、例外、コンパイラーの詳細なし。

コメンターは(感謝!)を指摘したように、static_assert引数では不可能である(しかし、いいだろう)。コンパイラーは、その状況での境界外のアクセスに関して別のエラーを私に与えました。



しかし醜い」ええと、コンマ演算子は醜いかもしれませんが、C ++ 11ではうまくいきます。すばらしい解決策は、C ++ 14に切り替えることです。:)
ドングリ

いくつかのツールはそれについて警告を吐きます」どのツールとどの警告?
ドングリ

以前はカンマ演算子が私の解決策でしたが、a)qacpp(コーディングガイドライン)などの静的コード分析によって警告され、b)ダウンストリームプロジェクトで奇妙な構文エラーが発生しました(理解できませんでした、カスタムアサートマクロの疑いがあります) 。さて、私は今それを避けようとしますが、それが仕事をしたことに同意します。
Borph

2
@Borphいいえ、static_assert依存をまったく使用できませんidxidx定数式を必要としない構成の評価を強制することにより、定数式を必要とするコンテキストで関数が使用されている場合にのみ、誤った値を診断できます。このようなコンテキスト外では、コンパイル時に値を確認することはできません。
クルミ

回答:


2

何かのようなもの

void assert_impl() { assert(false); } // Replace body with own implementation

#ifdef NDEBUG // Replace with own conditional
#define my_assert(condition) ((void)0)
#else
#define my_assert(condition) ((condition) ? (void()) : (assert_impl(), void()))
#endif

template<int Size>
struct Array {
  int m_vals[Size];
  constexpr const int& getElement( int idx ) const
  {
    return my_assert(idx < Size), m_vals[idx];
  }
};

定数式を必要とするコンテキストで使用すると、アサーションが失敗するとコンパイル時エラーが発生します(非constexpr関数を呼び出すため)。

それ以外の場合は、実行時にassert(または類似の)を呼び出して失敗します。

これは私が知る限りあなたができる最高のことです。の値を使用して、定数式を必要とするidxコンテキストの外でコンパイル時にチェックを強制する方法はありません。

コンマ演算子の構文は良くありませんが、C ++ 11 constexpr関数は非常に制限されています。

もちろん、すでに述べたように、関数が定数式を必要とするコンテキストで使用される場合、未定義の動作はとにかく診断されます。

あなたが知っている場合assert(または、あなたのアナログの)条件評価さへあれば定数式で禁止されているものには展開されませんtrueが、それが評価された場合はそうないfalse、あなたは直接の代わりにそれを使用することができmy_assert、私のビルドという間接をスキップ私のコードで。


1
反対票が与えられた場合、誰かが私の答えが間違っている場所を誰かに説明してもらえますか?
クルミ

これと@ecatmurソリューションは似ていますが、1つの答えを選択できます。あなたのものは簡単です。ただし、1つの発言:なぜ(void)0このNDEBUG場合とvoid()他の場合では?それとも本当に同じですか?
ボーフ

@Borph (void)0は何もしないでコンパイルされ、何もコンパイルされません(これはNDEBUG定義時に必要になりvoid()ます)が、条件演算子の2番目と3番目のオペランドの型が同じになるようにする必要がありますvoid
Bob__

@Borphは(void)0、すべての場合にも問題ないと思います。void()コンテキストに応じて、パラメータなしで戻り値のない関数の型として解析することもできるため、私は最初のケースでそれを置き換えました。2番目のケースの部分式では、そのように解析することはできません。
クルミ

3

コンマ式よりも、3項条件を使用できます。最初のオペランドはアサーション述語、2番目のオペランドは成功の式です。3番目のオペランドは任意の式(C ++ 11定数コンテキストでは使用できないものでも)なので、ラムダを使用してライブラリのASSERT機能を呼び出すことができます。

#define ASSERT_EXPR(pred, success)    \
    ((pred) ?                         \
     (success) :                      \
     [&]() -> decltype((success))     \
     {                                \
         ASSERT(false && (pred));     \
         struct nxg { nxg() {} } nxg; \
         return (success);            \
     }())

ラムダの本体の説明:

  • ASSERT(false && (pred)) アサーション機構が適切な式(文字列化のため)で呼び出されるようにすることです。
  • struct nxg { nxg() {} } nxgC ++ 17以降でNDEBUGラムダを使用してコンパイルした場合でも非安全であることを保証するために、将来の安全性を確保するconstexprために、アサーションがconst-evaluationコンテキストで実施されます。
  • return (success)2つの理由があります。2番目と3番目のオペランドが同じ型であることを確認するためと、ライブラリNDEBUGsuccess式を尊重する場合に関係なく式が返されるようにするためですpred。(predされ評価されていますが、その主張の述語が評価し、副作用がないために安価でなければなりません願っていと思います。)

使用例:

template<int Size>
struct Array {
  int m_vals[Size];
  constexpr int getElement( int idx ) const
  {
    return ASSERT_EXPR(idx < Size, m_vals[idx]);
  }
};

constexpr int I = Array<2>{1, 2}.getElement(1); // OK
constexpr int J = Array<2>{1, 2}.getElement(3); // fails

ああ!@walnutおよび初期化されていない変数は、アクセスされない場合、後で許可される可能性があります。より良い警備員を探してみます。ありがとう!
ecatmur

提案:s / [&]/ [&] -> decltype((success))参照を節約するため。
LF

@LF良い点、ありがとう!
ecatmur

pred評価は安いほうがいいですが、いつもそうとは限りません。したがって、一般的なASSERT_EXPRマクロとしてはお勧めしません。私は時々アサートで高価な呼び出しをします(例えば不変条件をチェックするため)
ボーフ

1
@Borph NDEBUGランタイムアサートを無効にするためにオンにした場合でも、コンパイル時のアサートをチェックする必要があると想定しています。失敗した場合のラムダの本体を非constexprにすることは、これを確実にする方法ですが、実行時に述語を評価して破棄するコストがかかりますNDEBUG。そうでない場合は、マクロNDEBUGをだけに定義できますreturn (success);
ecatmur

2

static_assertここでは使用できません。constexpr関数の引数は、定数式では使用できません。したがって、与えられた制約の下では問題に対する解決策はありません

ただし、2つの制約を曲げることで問題を解決できます。

  1. 使用しないstatic_assert(代わりに他の方法を使用してコンパイル時診断を生成する)、および

  2. コンマ演算子は「醜く、一部のツールはそれについて警告を吐きます」ということを無視してください。(醜さを示すことは、C ++ 11 constexpr関数の厳しい要件の不幸な結果です)

次に、通常の方法を使用できますassert

template <int Size>
struct Array {
  int m_vals[Size];
  constexpr const int& getElement(int idx) const
  {
    return assert(idx < Size), m_vals[idx];
  }
};

定数評価のコンテキストでは、のようなコンパイラエラーが発生しますerror: call to non-'constexpr' function 'void __assert_fail(const char*, const char*, unsigned int, const char*)'


-2

これは、ほとんどのコンパイラーで私にとってうまくいきますhttps : //godbolt.org/z/4nT2ub

template<int Size>
struct Array {
  int m_vals[Size];
  constexpr const int& getElement(int idx) const
  {
    assert(idx < Size);
    return m_vals[idx];
  }
};

未定義の動作を含めることができないstatic_assertため、現在は廃止さconstexprれています。したがって、配列インデックスをバストすると、コンパイラは適切なエラーを報告します。こちらをご覧ください

問題はassertです。実装が定義されていないマクロです。コンパイラがa constexprでない関数を使用すると失敗しますが、ご覧のとおり、3つの主要なコンパイラには問題はありません。


1
これはC ++ 11(OPの要件の1つ)では機能しません。コンパイラはおそらくデフォルトでC ++ 14以降を使用しています。
クルミ

C++11、これはGCCだけでは失敗しgodbolt.org/z/DB2zL3
マレクR

警告メッセージを見てください。MSVCは/std:c++11フラグをまったく受け入れません。ClangはC ++ 14を必要としますが、コードを許可します。Add -pedantic-errorsとClangは、純粋なC ++ 11コンパイラが与える適切なエラーを与えます。
クルミ

-3

C ++ 11には..だからどちらかすることはできませんidx定数または全く
だろう素敵な一個の機能を有する場合は、それぞれ
1つの関数に強制的にそれは一種の、このことができれば

template<int Size>
struct Array {
    int m_vals[Size];
    constexpr const  int& getElement( int idx ) const       
    {
    if constexpr(is_same_v<decltype(idx), const int>)
        static_assert( idx < Size, "out-of-bounds" ); // a no-go for non-constexpr calls
    else 
        assert( idx < Size ); // a no-go for constexpr funcs in c++11

    return m_vals[idx];
    }
};

int main() {                // E.g. up to next //

        Array<7> M;  
        int i=M.getElement(1);
}                           //

1
これが質問への回答になるかどうかはわかりませんが、コードはどのC ++バージョンでも有効ではありません。問題がないかどうかではないidxですconst。これが単なるC ++標準の提案である場合、それがどのように属しているかは回答セクションに表示されません。
クルミ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.