7

これは昨日からの私の質問のフォローアップです。書き込み専用コードについてのScottMeyersの警告が頭に浮かびます。std :: mapのキーまたは値にアクセスするために標準的なアルゴリズムを使用するという原則のアイデアは気に入っていますが、必要な構文は少しバロック的なIMHOです。マップのすべてのキーをベクトルにダンプしたいとします。次の宣言が与えられた場合、

typedef std::map<int, int> MyMap;
MyMap m;
std::vector<int> v;

どのコードがより保守しやすい(つまり、混乱が少ない可能性がある)か?

オプション1:

std::transform(m.begin(),
               m.end(),
               std::back_inserter(v),
               std::tr1::bind(&MyMap::value_type::first, _1));

オプション#2:

for (MyMap::iterator i = m.begin(); i != m.end(); ++i)
{
    v.push_back(i->first);
}

オプション1はより標準的なライブラリ風ですが、何が起こっているのかを理解するには、それを精神的に分解する必要があります。オプション2は、実行時のペナルティがわずかになる可能性がありますが、読みやすいようです。私はCPU時間を傷つけていないので、オプション2に傾いています。皆さんは同意しますか?考慮すべき3番目のオプションはありますか?

PSこの質問を書く過程で、(私のプロジェクトにとって)std :: mapのキーを読み取る最良の方法は、それらをサイドコンテナーに格納し、それを繰り返すことであると結論付けました。ただし、保守性の問題は依然として残っています。

4

7 に答える 7

11

明快さは常に賢さを打ち負かします。後で読めることを実行します。

標準コードが少しわかりにくいと思っているのはあなただけではありません。次の C++ 標準ではラムダ関数が導入されるため、標準アルゴリズムでより読みやすいコードを記述できます。

于 2008-12-17T22:18:09.987 に答える
5

最初のものは、2番目のものと同じくらい読みやすく、保守しやすいです-あなたが何をするか知っていればbind。Boost::Bind (本質的には と同じstd::tr1::bind) を使用してきたので、問題はありません。

TR1 が正式な標準の一部になると、有能な C++ プログラマーなら誰でも TR1 を理解できると安全に想定できます。それまでは難しいかもしれませんが、短期より長期のことを常に考えています。

于 2008-12-17T22:18:46.537 に答える
4

あなたは忘れましたusing namespace std::tr1::placeholders:P

正直なところ、このような単純なアルゴリズムの場合、後者のコードはおそらく保守が容易です。しかし、私は実際には前者を好む傾向があります(特に、C ++ 1xがラムダを提供する場合)。これは、ループを使用する命令型よりも個人的に好むプログラミングの機能的なスタイルを強調しているためです。

それは本当に別のストロークのことです。標準アルゴリズムは、複雑または汎用のいずれかである場合に最も役立ちますが、どちらでもありません。

ラムダを使用すると、次のようになります。

std::transform(m.begin(), m.end(), std::back_insterter(v),
               [](MyMap::value_type pair){ return pair.first; }
              );

そして実際には、私が好む別のアプローチがありますが、その冗長性のために:

using std::tr1::bind;
using std::tr1::placeholders::_1;
std::for_each(m.begin(), m.end(),
              bind(&std::vector<int>::push_back, v,
                   bind(&MyMap::value_type::first, _1)
                  )
             );

そしてラムダを使用すると(これはおそらく、すべてのオプションの中で最も適切で最も明示的です):

std::for_each(m.begin(), m.end(),
              [&v](MyMap::value_type pair){v.push_back(pair.first);}
             );
于 2008-12-17T22:24:11.377 に答える
3

私は2のために行くと言います)

m.end()パフォーマンスを向上させるために、ループから抜け出し、ベクトル内のスペースを予約することができます。

C++0x と範囲ベースの for ループが待ちきれません。それはあなたのループをさらに良くするでしょう。

于 2008-12-17T21:58:21.273 に答える
1

私はオプション#3に行きます:

#include <boost/range/adaptor/map.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>

boost::push_back(v, m | boost::adaptors::map_keys);

これには、次のような利点があります。

  1. 短いです

  2. 名前付き関数を使用してキーを取得します

  3. (潜在的に)より効率的です(boost::push_backを呼び出すことができるreserve()ためv

  4. v.begin()冗長なv.end()ペアは必要ありません。

他の方法は純粋な狂気です。

于 2012-03-23T23:28:14.020 に答える
1

昨日あなたの質問を見たとき、コードを理解するために2回見なければならなかったのはバインド(私はよく使用します)ではなく、map :: value_type::firstはあまり頻繁に使用する機会がありませんでした。「明快さは常に賢いものよりも優れている」ということに同意しますが、明快さの前に精通している必要があり、使用しないスタイルに精通することはありません...

また、オプション2は、意図された目的を理解するという点ではより明確ですが、バグをより簡単に隠すことができます(オプション1のエラーは、コンパイル時に表示される可能性が高くなります)。

于 2008-12-18T15:39:36.343 に答える
1

オプション #1 を使用します。Scott Meyers、Effective STL Item #43、181 ページを参照してください。

于 2008-12-18T00:55:24.537 に答える