通过引用传递 C++ 迭代器有什么问题?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/843996/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:32:48  来源:igfitidea点击:

What's wrong with passing C++ iterator by reference?

c++pass-by-referenceiterator

提问by Adrian McCarthy

I've written a few functions with a prototype like this:

我用这样的原型编写了一些函数:

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

The idea is that the caller would provide a range of characters, and the function would interpret the characters as an integer value and return it, leaving beginat one past the last-used character. For example:

这个想法是调用者将提供一个字符范围,函数将这些字符解释为一个整数值并返回它,留begin在最后使用的字符之后。例如:

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);

This would leave iset to 123 and p"pointing" at the space before foo.

这将i设置为 123 并p“指向”之前的空间foo

I've since been told (without explanation) that it's bad form to pass an iterator by reference. Is it bad form? If so, why?

从那以后,我被告知(没有解释)通过引用传递迭代器是不好的形式。形式不好吗?如果是这样,为什么?

采纳答案by Johannes Schaub - litb

There is nothing really wrong, but it will certainly limit the use of the template. You won't be able to just put an iterator returned by something else or generated like v.begin(), since those will be temporaries. You will always first have to make a local copy, which is some kind of boilerplate not really nice to have.

没有什么不对的,但它肯定会限制模板的使用。您不能只放置由其他东西返回或生成的迭代器v.begin(),因为它们将是临时的。您将始终首先必须制作本地副本,这是某种不太好的样板。

One way is to overload it:

一种方法是重载它:

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);
} 

Another option is to have an output iterator where the number will be written into:

另一种选择是有一个输出迭代器,数字将被写入:

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

You will have the return value to return the new input iterator. And you could then use a inserter iterator to put the parsed numbers into a vector or a pointer to put them directly into an integer or an array thereof if you already know the amount of numbers.

您将有返回值来返回新的输入迭代器。然后,如果您已经知道数字的数量,您可以使用插入器迭代器将解析的数字放入向量或指针中,以将它们直接放入整数或其数组中。

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

std::vector<int> numbers;
b = parse_integer(b, end, std::back_inserter(numbers));

回答by Reunanen

In general:

一般来说:

If you pass a non-constreference, the caller doesn't know if the iterator is being modified.

如果你传递一个非const引用,调用者不知道迭代器是否被修改。

You could pass a constreference, but usually iterators are small enough that it gives no advantage over passing by value.

您可以传递一个const引用,但通常迭代器足够小,与按值传递相比没有任何优势。

In your case:

在你的情况下:

I don't think there's anything wrong with what you do, except that it's not too standard-esque regarding iterator usage.

我不认为你所做的有什么问题,除了它在迭代器的使用方面不是太标准。

回答by ChrisW

When they say "don't pass by reference" maybe that's because it's more normal/idiomatic to pass iterators as value parameters, instead of passing them by const reference: which you did, for the second parameter.

当他们说“不要通过引用传递”时,可能是因为将迭代器作为值参数传递更正常/惯用,而不是通过 const 引用传递它们:您为第二个参数所做的。

In this example however you need to return two values: the parsed int value, and, the new/modified iterator value; and given that a function can't have two return codes, coding one of the return codes as a non-const reference is IMO normal.

但是,在此示例中,您需要返回两个值:解析的 int 值和新的/修改的迭代器值;并且鉴于函数不能有两个返回码,将返回码之一编码为非常量引用是 IMO 正常的。

An alternative would be to code it something like this:

另一种方法是编写如下代码:

//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)
{
}

回答by Michael Burr

In my opinion, if you want to do this the argument should be a pointer to the iterator you'll be changing. I'm not a big fan of non-const reference arguments because they hide the fact that the passed parameter might change. I know there's a lot of C++ users who disagree with my opinion on this - and that's fine.

在我看来,如果你想这样做,参数应该是一个指向你将要改变的迭代器的指针。我不是非常量引用参数的忠实粉丝,因为它们隐藏了传递的参数可能会改变的事实。我知道有很多 C++ 用户不同意我对此的看法 - 这很好。

However, in this case it's socommon for iterators to be treated as value arguments that I think it's a particularly bad idea to pass iterators by non-const reference and modify the passed iterator. It just goes against the idiomatic way iterators are usually used.

但是,在这种情况下,将迭代器视为值参数非常普遍,我认为通过非常量引用传递迭代器并修改传递的迭代器是一个特别糟糕的主意。它只是违背了迭代器通常使用的惯用方式。

Since there is a great way to do what you want that doesn't have this problem, I think you should use it:

由于有一种很好的方法可以做你想做的事情而没有这个问题,我认为你应该使用它:

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

Now a caller would have to do:

现在调用者必须执行以下操作:

int i = parse_integer(&p, end);

And it'll be obvious that the iterator can be changed.

很明显,迭代器可以改变。

By the way, I also like litb's suggestionof returning the new iterator and putting the parsed values into a location specified by an output iterator.

顺便说一句,我也喜欢litb 的建议,即返回新迭代器并将解析后的值放入输出迭代器指定的位置。

回答by Edward Loper

In this context, I think that passing an iterator by reference is perfectly sensible, as long as it's well-documented.

在这种情况下,我认为通过引用传递迭代器是完全明智的,只要它有据可查。

It's worth noting that your approach (passing an iterator by reference to keep track of where you are when tokenizing a stream) is exactly the approach that is taken by boost::tokenizer. In particular, see the definition of the TokenizerFunction Concept. Overall, I find boost::tokenizer to be pretty well designed and well thought out.

值得注意的是,您的方法(通过引用传递迭代器以跟踪标记流时的位置)正是boost::tokenizer所采用的方法。特别是,请参阅TokenizerFunction 概念的定义。总的来说,我发现 boost::tokenizer 设计得非常好,经过深思熟虑。

回答by Edward Loper

I think the Standard Library algorithms pass iterators by value exclusively (someone will now post an obvious exception to this) - this may be the origin of the idea. Of course, nothing says that your own code has to look like the Standard Library!

我认为标准库算法专门按值传递迭代器(现在有人会发布一个明显的例外) - 这可能是这个想法的起源。当然,没有什么说你自己的代码必须看起来像标准库!

回答by dirkgently

Your function declaration's second parameter is missing the reference, is it?

您的函数声明的第二个参数缺少引用,是吗?

Anyway, back to your question: No, I haven't ever read anything that says you should not pass iterators by reference. The problem with references is that they allow you to change the referenced object. In this case, if you are to change the iterator, you are potentially screwing up the entire sequence beyond that point thereby rendering further processing impossible.

无论如何,回到你的问题:不,我从来没有读过任何说你不应该通过引用传递迭代器的东西。引用的问题在于它们允许您更改引用的对象。在这种情况下,如果您要更改迭代器,则可能会在该点之后搞砸整个序列,从而无法进行进一步处理。

Just one suggestion: type your parameters carefully.

只有一个建议:仔细输入参数。