読者です 読者をやめる 読者になる 読者になる

voidもしくはboolを返す

書いているC++プログラムの一か所に出てきた既視感のある部分についての備忘録。

該当部分は次の4行。

    struct bool_proxy {
        explicit operator bool() const  { return true; }
        friend bool operator ,(bool b, const bool_proxy&)   { return b; }
    };

こういう使い方をする。

    template <typename F>
    void foo(F&& func, int begin, int end)
    {
        bool_proxy   bp;        // <--  これ(後から追加)
        for ( auto i = begin; i < end; ++i )
        {
            //  std::forward<F>(func)(i);    // <--  最初はこうだった
            if ( !(std::forward<F>(func)(i), bp) )
                    break;
        }
    }

//----------------------------------------------------------
    std::vector<int>  vec;

    auto void_expr = [&](int a)  {     //もともとあったファンクタ
        vec.push_back(a);
    };

    auto bool_expr = [&](int a)  {     //追加したファンクタ
        if ( a < 5 )    vec.push_back(a);
        return a < 5;
    };

    foo(void_expr, -99, 100);    // -99 ~ +99
    foo(bool_expr, -99, 100);    // -99 ~ +4

関数fooはループ中でコールバックfuncを呼び出すが、「最初はこうだった」とコメントアウトしている通り当初は無条件に呼び出すだけで、funcの戻り値もvoidだった。その後条件によってループをbreakしたいという要求が出てきたので、bool値を返すファンクタを作った(falseが返ってきたときbreak)。
しかし、

  • すべての場合にそういう条件をつけたい訳ではない
  • もともと使っていたファンクタの戻り値型をvoidからboolに直すのは面倒だ

そこでfoo<F>オーバーロードしよう思ったが、以下のようなことを考えてやりたくなくなった。

  • 一部分の挙動が違うだけのオーバーロード関数を作るのはイヤだ
  • 複数の異なる型のコールバックを受け取るときは組み合わせが多くなる
  • いろんなシグネチャパターンのコールバックに対応するとしたら面倒だ

voidな式はtrueと評価し、boolな式はそれ自体として評価する方法はないかと考えた結果、最初に示したbool_proxyのアイデアを思いついた。「イヤだ」と感じた3つの点は避けられたが、既視感があるようなないような、罠があるようなないような、もっと全然簡単な方法があるようなモヤモヤした状態のまま使っている。

とりあえずbool_proxyという名前がどこか間違っているような気がしてならない。