4

C++ では、関数が多くの (数百または数千の) 値を作成するたびに、関数が出力値で満たす配列を呼び出し元に渡すようにしていました。

void computeValues(int input, std::vector<int>& output);

そのため、関数はoutput計算した値でベクトルを埋めます。しかし、私が今気付いているように、これは本当に良い C++ スタイルではありません。

次の関数シグネチャは、 の使用をコミットしないため、より優れていstd::vectorますが、任意のコンテナーを使用できます。

void computeValues(int input, std::insert_iterator<int> outputInserter);

これで、発信者は次のように呼び出すことができますinserter:

std::vector<int> values; // or could use deque, list, map, ...
computeValues(input, std::back_inserter(values));

繰り返しますが、具体的に使用することにコミットしていませんstd::vector。これは良いことです。なぜなら、ユーザーは a などの値を必要とするだけかもしれないからです(値または参照std::setで渡す必要がありますか?)iterator

私の質問は次のとおりですinsert_iterator。それを行う正しい方法または標準的な方法はありますか? それとももっと良いものがありますか?

編集:質問を編集して、2 つまたは 3 つの値を返すことについて話しているのではなく、数百または数千の値を返すことについて話していることを明確にしました。(特定のディレクトリで見つかったすべてのファイル、またはグラフ内のすべてのエッジなどを返すと想像してください。)

4

9 に答える 9

7

編集への応答:まあ、数百、数千の if 値を返す必要がある場合、もちろんタプルは適していません。次に、イテレータを使用してソリューションを選択するのが最善ですが、特定のイテレータ タイプを使用しないことをお勧めします。


イテレータを使用する場合は、できるだけ汎用的に使用する必要があります。あなたの関数では、のような挿入イテレータを使用しましたinsert_iterator< vector<int> >。一般性を失いました。次のようにします。

template<typename OutputIterator>
void computeValues(int input, OutputIterator output) {
    ...
}

何を与えても、すぐに機能します。ただし、戻り値セットに異なる型がある場合は機能しません。その場合、タプルを使用できます。std::tuple次の C++ 標準のようにも利用できます。

boost::tuple<int, bool, char> computeValues(int input) { 
    ....
}

値の量が可変長で、値の型が (int、bool、char) などの固定セットからのものである場合、 のコンテナーを調べることができますboost::variant。ただし、これは呼び出し側でのみ変更されることを意味します。上記のイテレータ スタイルを維持できます。

std::vector< boost::variant<int, bool, char> > data;
computeValues(42, std::back_inserter(data));
于 2009-02-24T22:26:16.497 に答える
6

ベクトルへのスマート ポインターを返すことができます。それはうまくいくはずで、ベクトルのコピーは作成されません。

プログラムの残りの部分でスマート ポインターを保持したくない場合は、関数を呼び出す前にベクトルを作成し、両方のベクトルを交換することができます。

于 2009-02-24T22:42:37.597 に答える
3

実際、ベクトルを渡す古い方法には、多くの推奨事項があります。これは、効率的で、信頼性が高く、理解しやすいものです。欠点は現実にありますが、すべての場合に等しく当てはまるわけではありません。人々は本当に std::set または list のデータを欲しがるでしょうか? 最初に変数に代入する手間をかけずに、数字の長いリストを本当に使用したいのでしょうか (パラメーターではなく「リターン」を介して何かを返す理由の 1 つです)。ジェネリックであることは良いことですが、プログラミング時間には償えないかもしれないコストがあります。

于 2009-02-25T02:41:25.027 に答える
2

オブジェクトのグループがある場合、そのオブジェクトのグループで機能するメソッドが少なくともいくつかある可能性があります (そうでない場合、それらで何をしていますか?)

その場合、前述のオブジェクトとメソッドの両方を含むクラスにこれらのメソッドを含めることは理にかなっています。

それが理にかなっていて、そのようなクラスがある場合は、それを返します。

複数の値を返すことができればいいのにと思うことはほとんどありません。メソッドは小さなことを 1 つだけ実行する必要があるという事実により、パラメーターと戻り値は関係を持つ傾向があり、それらを含むクラスに値しないことが多いため、複数の値を返すことはめったにありません (おそらく私は 20 年間で 5 回それを望んでいました。代わりにリファクタリングを行うたびに、より良い結果が得られ、最初の試みが標準以下であることに気付きました。)

于 2009-02-24T22:18:09.107 に答える
1

もう 1 つのオプションは boost::tuple です: http://www.boost.org/doc/libs/1_38_0/libs/tuple/doc/tuple_users_guide.html

int x, y;
boost::tie(x,y) = bar();
于 2009-02-24T22:09:57.363 に答える
1

私は次のようなものを使用します

std::auto_ptr<std::vector<int> > computeValues(int input);
{
   std::auto_ptr<std::vector<int> > r(new std::vector<int>);
   r->push_back(...) // Hundreds of these
   return r;
}

戻り値のコピー オーバーヘッドやリークのリスクはありません (呼び出し元で auto_ptr を正しく使用している場合)。

于 2009-02-24T22:54:30.313 に答える
1

insert_iteratorはパラメーターのコンテナーを必要とするテンプレートであるため、 insert_iteratorを使用した例は機能しません。あなたはそれを宣言することができます

void computeValues(int input, std::insert_iterator<vector<int> > outputInserter);

また

template<class Container>
void computeValues(int input, std::insert_iterator<Container> outputInserter);

1 つ目は vector<int> の実装に縛り付けますが、最初のコードより明らかな利点はありません。2 番目の方法は制限が緩いですが、テンプレートとして実装すると他の制約が生じるため、あまり望ましくない選択になる可能性があります。

于 2009-02-24T22:30:31.557 に答える
1
  • 標準のコンテナーは、同種のオブジェクト (返品可能) に対して機能します。
  • 標準ライブラリの方法は、コンテナーからアルゴリズムを抽象化し、反復子を使用して間のギャップを埋めることです。
  • 複数の型を渡す必要がある場合は、構造体/クラスを考えてください。

私の質問は次のとおりです: insert_iterator は正しい方法ですか、それとも標準的な方法ですか?

はい。それ以外の場合、コンテナ内に少なくとも計算された値と同じ数の要素を持たない場合。特に、ストリームに書き込みたい場合、これは常に可能であるとは限りません。だから、あなたは良いです。

于 2009-02-24T22:11:10.480 に答える
0

あなたの新しい解決策はより一般的で、より良いスタイルだと思います。C ++のスタイル、使いやすさと効率についてあまり心配するかどうかはわかりません。

大量のアイテムを返し、サイズがわかっている場合は、ベクトルを使用すると、1回の割り当てでメモリを予約できますが、それだけの価値がある場合とない場合があります。

于 2009-02-25T13:53:06.520 に答える