无法将迭代器传递给接受指针的函数

3
在《Effective STL》的第16条建议中,它说我们应该避免将迭代器传递给接受指针的函数。有人可以为我解释一下吗?
void doSomething(const int* pInts, size_t numInts);
vector<int> v;
...
doSomething(&v[0],v.size()) //correct
doSomething(v.begin(),v.size()) //incorrect
4个回答

11
在标准库的一些早期实现中,迭代器被实现为指针。依赖这种属性的代码会在迭代器变为非指针时停止工作。因此,不应该将迭代器传递到需要指针的位置,因为如果编译通过,那只是偶然发生的。迭代器并不一定是指针。如果它是一个指针,那么在一些后续版本(库、编译器等)中,它可能不再是一个指针。

向量迭代器在发布模式下通常是指针。如果您将迭代器传递给接受指针的函数,则会得到在发布模式下编译但在调试模式下无法编译的代码 :) - fredoverflow
1
@FredOverflow:可以说,它们应该是指针的简单包装器,以避免不一致性。 - Steve Jessop
2
指针不仅不一致,而且没有std::作为它们关联的命名空间。 - MSalters
@MSalters:有趣的观点,是否应该将实现的迭代器放在不同的命名空间中,以防止程序错误地依赖于与“std”关联? - Steve Jessop
@Steve:更深层次的问题是,一般而言,设计时是否应该考虑“支持自动导入模块”?我认为这可能很困难! - Cheers and hth. - Alf
@Alf:同意,覆盖所有内容确实很困难。但是,“One”相对于标准库实现者有一个优势,如果我想的话,我可以保证我的容器迭代器与容器在同一命名空间中或不在同一命名空间中。然后我就不需要帮助我的用户进行ADL安全性,因为如果他们意外地假设了它,那也没关系。当然,实现不能使标准允许的每个反常或令人惊讶的选择,所以我认为这最多只是“可能帮助用户”,而不是“应该”。 - Steve Jessop

2

很简单。

该函数接受指针,而不是迭代器。

如果您试图传递一个迭代器,则应该预期函数调用将失败。方形钉子,圆形孔。


注:

  • 反之则不同,因为指针可以被视为一种迭代器。通常,一个接受迭代器的函数可以传递指针。

  • 由于实现细节,您的代码在某些非常特定的情况下可能会编译通过。不要被这个所欺骗:它仍然是错误的代码!


正如Alf所指出的那样,它并不保证会失败,因为迭代器可能是一个指针。然而,vector是唯一一个其迭代器可以是指针的标准容器。 - Steve Jessop
Occam的剃刀原则:“除非必要,实体不应该被增加。”(我强调)。在这种情况下,更简单的陈述是错误的。 - Steve Jessop
@Steve:那就“对孩子撒谎”吧。 - Lightness Races in Orbit
好的,虽然我认为我仍然更喜欢Alf对同样事物的解释。 - Steve Jessop
@Steve:那当然是你的权利!我选择强调迭代器和指针并不相同,而不是强调只有在极少数情况下你可以假装它们是相同的。 - Lightness Races in Orbit

1

因为迭代器不仅仅是一个原始指针(尽管某些底层实现可能是指针)。有许多类型的迭代器:前向迭代器、双向迭代器、随机访问迭代器。最接近指针的迭代器是像 vector 或 deque 所拥有的随机访问迭代器。


0
整个迭代器的概念是为了实现灵活的数据存储,这些数据不是连续存储在内存中的。例如,增加一个迭代器可以移动到数组中的另一个“页面”,该数组作为单独的元素块存储。
如果调用一个需要指针的函数,被调用的函数将简单地对地址进行指针算术运算,当你增加它时(因此它不会遵循链接列表中的链接)。
将迭代器传递给需要指针的函数是行不通的。只有在确定容器中的数据是连续存储的情况下,才能解引用第一个元素然后取其地址。这对于向量可以起作用,但如果容器是列表,则无法起作用。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接