5

ベクトルに保存されているサーバー名のリストがあり、1 つが正常に応答するまで一度に 1 つずつ連絡したいとします。STL の find_if アルゴリズムを次のように使用することを考えていました。

find_if(serverNames.begin(), serverNames.end(), ContactServer());

ここで、ContactServer は述語関数オブジェクトです。
一方では、述語が同じサーバー名に対して常に同じ結果を返すとは限らないため、問題があります (サーバーのダウンタイム、ネットワークの問題などのため)。ただし、述語のどのコピーが使用されても (つまり、述語に実際の状態がない)、同じ結果が返されるため、この場合、状態保持述語の元の問題は関係ありません。

あなたは何を言っていますか?

4

8 に答える 8

4

私はそれのために行くと思います。

私が心配する唯一のことは、その可読性 (したがって保守性) です。私には、「接続できる最初のサーバーを見つけてください」のような内容が表示されますが、これは完全に理にかなっています。

ContactServer述語であることを示すために名前を変更したい場合があります。CanContactServer? (しかし、人々は隠れた副作用について不平を言うでしょう。うーん...)

于 2008-08-26T09:17:59.637 に答える
4

これは、まさに STL アルゴリズムの目的です。これはまったく虐待ではありません。さらに、非常に読みやすいです。そうでないと言う人は null にリダイレクトします。

于 2008-09-22T04:08:22.953 に答える
2

私の意見では、この の使用std::find_ifは少し誤解を招くものです。このコードを読んだとき、副作用が発生することはないと思います。サーバー名が見つかることだけを期待しています。の結果が破棄されるという事実find_ifも、コードが本当に正しいかどうか疑問に思うでしょう。述語に別の名前を付けた方が意図が明確になるかもしれませんが、問題はもっと根本的なところにあると思います。

ほとんどの人にとって、find_ifクエリアルゴリズムであり、変更アルゴリズムではありません。繰り返し処理される値を実際に変更しているわけではありませんが、アプリケーションのグローバルな状態を変更しています (この場合、離れたサーバーの状態を変更している可能性さえあります)。

そのような場合、特に C++11 が範囲ベースの for ループを導入した今では、おそらく手動ループを使い続けるでしょう。

for (std::string const & name : serverNames)
{
    if (ContactServer(name)) break;
}

別の解決策は、次のような意図をより明確に伝える名前の関数にこれをカプセル化するapply_untilことです。

template <typename InputIterator, typename Function>
void apply_until(InputIterator first, InputIterator last, Function f)
{
    std::find_if(first, last, f);
    // or
    // while (first != last)
    // {
    //     if (f(*first)) break;
    //
    //     ++first;
    // }
}
}

しかし、おそらく私は過度に純粋主義的です:)!

于 2012-05-25T16:08:05.563 に答える
0

C++ 標準の次のバージョンでは、述語が同じ入力に対して常に同じ値を返す必要があるという事実に関して、明示的な制限を見つけることができませんでした。セクション 25 (パラグラフ 7 から 10) を調べました。

あなたの場合のように、ある呼び出しから別の呼び出しに変更される可能性のある値を返すメソッドは、揮発性である必要があります (7.1.6.1/11 から: 「揮発性は、オブジェクトの値が実装によって検出できない手段によって変更される」)。

述語は、「逆参照されたイテレータを通じて非定数関数を適用してはならない」(パラグラフ 7 および 8)。これは、不揮発性メソッドを使用する必要がなく、ユースケースが標準に関して問題がないことを意味します。

文言が「述語はconst関数を適用する必要があります...」またはそのようなものである場合、「const volatile」関数は問題ないと結論付けていたでしょう。しかし、そうではありません。

于 2008-09-02T11:21:20.067 に答える
0

ここでは find_if が正しい選択のようです。この状況では、述語はステートフルではありません。

于 2008-09-19T15:52:36.670 に答える
0

そのためじゃないfind_ifの?

ただし、イテレータを反復処理すると、すべてのサーバーが検出されることに注意してください-しかし、それを行うつもりはありません(OPによると)。

于 2008-08-26T09:21:00.020 に答える
0

ただし、述語のどのコピーが使用されても (つまり、述語に実際の状態がない)、同じ結果が返されるため、この場合、状態保持述語の元の問題は関係ありません。

では、どこに問題があるのでしょうか。関数オブジェクトは必ずしもステートフルである必要はありません。実際には、このような状況では関数ポインターの代わりに関数オブジェクトを使用するのがベスト プラクティスです。find_ifあなたの場合、関数オブジェクトのインスタンス化と呼び出しは関数テンプレートであり、コンパイラはファンクター用に独自のバージョンを生成するため、オーバーヘッドがまったくない場合があります。

一方、関数ポインターを使用すると、間接化が発生します。

于 2008-08-26T09:22:30.797 に答える
0

std::for_each は、こ​​れに適した候補かもしれません。

1) コピーされた後、同じ関数オブジェクトが各要素で使用され、すべての要素が処理された後、更新される可能性のある関数オブジェクトのコピーがユーザーに返されます。

2)私の意見では、呼び出しの可読性も向上します。

関数オブジェクトと for_each 呼び出しは次のようになります。


struct AttemptServerContact {
  bool        server_contacted;
  std::string active_server;    // name of server contacted

  AttemptServerContact() : server_contacted(false) {}

  void operator()(Server& s) {
    if (!server_contacted) {
      //attempt to contact s
      //if successful, set server_contacted and active_server
    }
  }
};

AttemptServerContact func;
func = std::for_each(serverNames.begin(), serverNames.end(), func);
//func.server_contacted and func.active_server contain server information.
于 2008-09-19T15:50:42.777 に答える