C++ イテレータを参照渡しすることの何が問題なのでしょうか?
-
20-08-2019 - |
質問
次のようなプロトタイプを使用していくつかの関数を作成しました。
template <typename input_iterator>
int parse_integer(input_iterator &begin, input_iterator end);
その考え方は、呼び出し元が文字の範囲を指定し、関数がその文字を整数値として解釈してそれを返し、最後に使用された文字の 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);
これでは出発します i
123に設定すると、 p
前のスペースを「指して」 foo
.
それ以来、(説明なしで) イテレータを参照によって渡すのは悪い形式であると言われてきました。フォームが悪いのでしょうか?もしそうなら、なぜですか?
解決
は、本当に何も悪いことはありませんが、それは確かに、テンプレートの使用を制限します。これらは一時となりますので、あなたは、ちょうどイテレータ何か他のもので返されるかv.begin()
のように生成さを置くことはできません。あなたは、常に最初に持っている本当に素敵ではない定型のいくつかの種類である、ローカルコピーを作成する必要があります。
一つの方法は、それをオーバーロードすることです。
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));
他のヒント
の一般的にの
あなたは不合格した場合 - 。const
参照をイテレータが変更されている場合、呼び出し側は知りません。
あなたは<=>参照を渡すことができますが、通常、イテレータはそれが値渡しの上に何の利点を与えないことを十分に小さいます。
をお使いの場合:の
私はそれがあまりにも標準風に関するイテレータの使用状況ではないことを除いて、あなたは何をすべきかに何か問題がないと思う。
彼らはそれが代わりにconst参照することによって、それらを渡すので、値パラメータとしてイテレータを渡す慣用/より正常なので、多分それはだ「参照渡していない」と言うときは:あなたがやっている、2番目のパラメータの
この例では、しかし、あなたは、2つの値を返す必要があります:解析されたint型の値、および、新しい/変更イテレータ値を、そして関数は、2つの戻りコードを持つことができないことを考えると、非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)
{
}
私の意見では、あなたがこれをしたい場合は、引数を使用すると、変更することがありますイテレータへのポインタでなければなりません。彼らは渡されたパラメータが変更される可能性があるという事実を隠すので、私は非const参照引数の大ファンではないんです。私はこれについての私の意見に同意できないC ++ユーザーの多くがあります知っている - それは大丈夫です。
。ただし、この場合には、私はそれが非const参照によってイテレータを渡し、渡されたイテレータを変更することが特に悪いアイデアだと思う値の引数として扱われるイテレータのためののようにのが一般的です。それはちょうどイテレータは通常使用されている慣用的な方法に反します。
あなたはそれがこの問題を持っていませんやりたいのに最適な方法があるので、、私はあなたがそれを使用するべきだと思います:
template <typename input_iterator>
int parse_integer(input_iterator* begin, input_iterator end);
さて、発信者がしなければならないでしょう。
int i = parse_integer(&p, end);
そして、それはイテレータを変更することができることは明らかだろう。
ちなみに、私はまた、 litbの提案が好き新しいイテレータを返すと、出力反復子によって指定された場所に解析値を置くする。
この文脈では、十分に文書化されている限り、反復子を参照によって渡すことは完全に賢明であると思います。
あなたのアプローチ(ストリームをトークン化するときにどこにいるかを追跡するために参照によってイテレータを渡す)は、まさに ブースト::トークナイザー. 。特に、の定義を参照してください。 トークナイザー関数の概念. 。全体として、boost::tokenizer は非常によく設計され、よく考えられていると思います。
私は標準ライブラリのアルゴリズムは(誰かが今、これに明白な例外を掲載します)専用の値でイテレータを渡すと思います - これはアイデアの起源かもしれません。もちろん、何も独自のコードは標準ライブラリのように見えるように持っていることを述べていません!
あなたの関数宣言の2番目のパラメータは参照が不足している、それはありますか?
とにかく、戻ってあなたの質問に:いいえ、私は今まであなたが参照してイテレータを渡すべきではないと言うものを読んでいません。参照の問題は、彼らはあなたが参照されたオブジェクトを変更することができるようにということです。あなたはイテレータを変更する場合は、この場合、あなたが潜在的に、それによって不可能さらにレンダリング処理をそのポイントを超えてシーケンス全体を台無しにされます。
ただ、1つの提案:慎重にパラメータを入力し、
。