46

次のようなプロトタイプを使用して、いくつかの関数を作成しました。

template <typename input_iterator>
int parse_integer(input_iterator &begin, input_iterator end);

呼び出し元が文字の範囲を提供し、関数が文字を整数値として解釈して返しbegin、最後に使用された文字の 1 つ後ろに残すという考え方です。例えば:

std::string sample_text("123 foo bar");
std::string::const_iterator p(sample_text.begin());
std::string::const_iterator end(sample_text.end());
int i = parse_integer(p, end);

これはi123 に設定されたままになりp、 の前のスペースを「指す」ことになりfooます。

それ以来、参照によってイテレータを渡すのは悪い形式だと (説明なしに) 言われました。フォームが悪いのでしょうか?もしそうなら、なぜですか?

4

7 に答える 7

36

特に問題はありませんが、テンプレートの使用が制限されることは間違いありません。v.begin()一時的なものになるため、他の何かによって返された、または のように生成されたイテレータを配置することはできません。常に最初にローカル コピーを作成する必要があります。

1 つの方法は、オーバーロードすることです。

int parse_integer(input_iterator begin, input_iterator end, 
                  input_iterator &newbegin);

template<typename input_iterator>
int parse_integer(input_iterator begin, input_iterator end) {
    return parse_integer(begin, end, begin);
} 

別のオプションは、数値が書き込まれる出力反復子を持つことです。

template<typename input_iterator, typename output_iterator>
input_iterator parse_integer(input_iterator begin, input_iterator end,
                             output_iterator out);

新しい入力反復子を返す戻り値があります。そして、数値の量がすでにわかっている場合は、挿入反復子を使用して、解析された数値をベクトルまたはポインターに配置し、整数またはその配列に直接配置することができます。

int i;
b = parse_integer(b, end, &i);

std::vector<int> numbers;
b = parse_integer(b, end, std::back_inserter(numbers));
于 2009-05-09T21:12:12.143 に答える
5

一般に:

非参照を渡すconstと、呼び出し元は反復子が変更されているかどうかを知りません。

参照を渡すこともできconstますが、通常、反復子は十分に小さいため、値渡しよりも利点がありません。

あなたの場合:

イテレータの使用に関して標準的すぎないことを除けば、あなたの行動に問題はないと思います。

于 2009-05-09T20:35:12.373 に答える
2

この文脈では、十分に文書化されている限り、参照によってイテレータを渡すことは完全に賢明であると思います。

あなたのアプローチ(ストリームをトークン化するときにあなたがどこにいるかを追跡するために参照によってイテレーターを渡す)は、boost::tokenizerによって採用されるアプローチとまったく同じであることに注意してください。特に、TokenizerFunctionの概念の定義を参照してください。全体として、boost :: tokenizerはかなりうまく設計され、よく考えられていると思います。

于 2011-12-20T16:51:58.020 に答える
2

彼らが「参照渡ししないでください」と言うとき、それはおそらく、反復子を const 参照で渡すのではなく、値パラメーターとして渡す方が通常/慣用的であるためです。

ただし、この例では、解析された int 値と、新しい/変更されたイテレータ値の 2 つの値を返す必要があります。関数が 2 つのリターン コードを持つことはできないため、リターン コードの 1 つを非 const 参照としてコーディングすることは IMO の通常の動作です。

別の方法は、次のようにコーディングすることです。

//Comment: the return code is a pair of values, i.e. the parsed int and etc ...
pair<int, input_iterator> parse(input_iterator start, input_iterator end)
{
}
于 2009-05-09T20:46:57.807 に答える
2

私の意見では、これを行いたい場合、引数は変更するイテレータへのポインタである必要があります。非 const 参照引数は、渡されたパラメーターが変更される可能性があるという事実を隠しているため、あまり好きではありません。これに関する私の意見に同意しない C++ ユーザーがたくさんいることは知っていますが、それは問題ありません。

ただし、この場合、イテレータが値の引数として扱われることが非常に一般的であるため、イテレータを非 const 参照で渡し、渡されたイテレータを変更することは特に悪い考えだと思います。これは、イテレータが通常使用される慣用的な方法に反するだけです。

この問題を起こさずにやりたいことを行う素晴らしい方法があるので、それを使用する必要があると思います。

template <typename input_iterator>
int parse_integer(input_iterator* begin, input_iterator end);

ここで、呼び出し元は次のことを行う必要があります。

int i = parse_integer(&p, end);

そして、イテレータを変更できることは明らかです。

ところで、新しい反復子を返し、解析された値を出力反復子によって指定された場所に配置するというlitb の提案も気に入っています。

于 2009-05-09T22:10:24.407 に答える
1

標準ライブラリのアルゴリズムはイテレータを排他的に値渡ししていると思います (誰かがこれに対する明らかな例外を投稿するでしょう) - これがアイデアの起源かもしれません。もちろん、自分のコードが標準ライブラリのように見えなければならないということはありません!

于 2009-05-09T20:46:16.747 に答える
-1

関数宣言の 2 番目のパラメーターに参照がありませんね。

とにかく、あなたの質問に戻ります。いいえ、参照によってイテレータを渡してはいけないと言っているものを読んだことがありません。参照の問題は、参照されるオブジェクトを変更できることです。この場合、イテレータを変更すると、その時点以降のシーケンス全体が台無しになり、それ以上の処理が不可能になる可能性があります。

パラメータを慎重に入力してください。

于 2009-05-09T20:39:04.703 に答える