46

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

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

这个想法是调用者将提供一系列字符,并且该函数会将字符解释为整数值并返回它,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);

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

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

4

7 回答 7

36

没有什么真正的错误,但它肯定会限制模板的使用。您将不能只放置由其他东西返回或生成的迭代器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));
于 2009-05-09T21:12:12.143 回答
5

一般来说:

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

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

在你的情况下:

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

于 2009-05-09T20:35:12.373 回答
2

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

然而,在这个例子中,您需要返回两个值:解析的 int 值和新的/修改的迭代器值;并且鉴于一个函数不能有两个返回码,将其中一个返回码编码为非常量引用是 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

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

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

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

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 回答
2

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

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

于 2011-12-20T16:51:58.020 回答
1

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

于 2009-05-09T20:46:16.747 回答
-1

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

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

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

于 2009-05-09T20:39:04.703 回答