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

时间:2009-05-09 20:31:20

标签: c++ pass-by-reference iterator

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

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

这个想法是调用者会提供一系列字符,并且该函数会将字符解释为整数值并返回它,从最后使用的字符开始。例如:

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之前的空格。

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

7 个答案:

答案 0 :(得分:30)

没有什么是错的,但肯定会限制模板的使用。您将无法仅将其他东西返回的迭代器或像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));

答案 1 :(得分:4)

一般情况下:

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

你可以传递const引用,但通常迭代器足够小,它没有优于传递值。

在您的情况下:

我认为你所做的事情没有任何问题,只是它在迭代器使用方面不太标准化。

答案 2 :(得分:2)

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

在这个例子中,你需要返回两个值:解析的int值,以及new / modified迭代器值;并且假设函数不能有两个返回码,则将其中一个返回码编码为非常量参考值是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)
{
}

答案 3 :(得分:2)

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

但是,在这种情况下,迭代器的 so 通常被视为值参数,我认为通过非const引用传递迭代器并修改传递的迭代器是一个特别糟糕的主意。它恰好违背了通常使用迭代器的惯用方法。

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

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

现在调用者必须这样做:

int i = parse_integer(&p, end);

很明显迭代器可以改变。

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

答案 4 :(得分:2)

在这种情况下,我认为通过引用传递迭代器是完全合理的,只要它有详细记录。

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

答案 5 :(得分:1)

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

答案 6 :(得分:-1)

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

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

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