为什么std::accumulate对于标准数组的行为是这样的?

Why does std::accumulate behave like this with standard arrays?

本文关键字:数组 标准 accumulate std 于标准 为什么      更新时间:2023-10-16

我刚刚进入c++,我想我有一个处理指针,但std::accumulate()让我困惑。

给定数组:

int a[3] = { 5, 6, 7 };

我想用std::accumulate()求和数组的值,所以我传递给它一个指向第一个元素的指针,然后是最后一个元素,然后是累加器的起始值。

std::accumulate(a, a + 2, 0);
std::accumulate(&a[0], &a[2], 0);

哎呀,这两种方法都只返回前两个元素的和:11

另一方面,如果第二个参数是一个无意义的指针,就越界了…

std::accumulate(a, a + 3, 0);
std::accumulate(&a[0], &a[3], 0);

…返回正确的18

有人能解释一下吗?我意识到我可以避免使用简单的数组,但这不是重点。

c++范围定义为[first, last),所有STL算法都是这样工作的。在本例中,std::accumulate将迭代器定义范围内的所有元素加在一起,从first开始,到last结束,没有对其解引用。

因此,像std::accumulate(a, a+3, 0)一样调用它实际上是正确的,并且等于用std::accumulate(begin(a), end(a), 0)调用它。

还需要注意的是,这并不违反"没有指向已分配数组外部的指针"规则,因为对于指向最后一个元素后面的指针有一个特定的异常。

std::accumulate与大多数STL算法一样,接受容器最后一个元素的迭代器。在本例中,该迭代器为&a[3]。您可能希望使用std::begin(), std::end(),因为它适用于所有容器,并且不易出错。此外,由于数组上的std::begin只是一个指针,您可以总是说std::begin()+k之类的东西。所以,你不会失去任何灵活性。

结束迭代器比最后一个元素多一个,因为在数组中"ranges"是半开的。这意味着包含第一个元素而不包含最后一个元素。

除此之外,我建议您使用c++11的开始和结束自由函数。

我不知道迭代器(包括原始指针和STL迭代器)这种约定的历史原因。但我的意见是,这是一致的方式,我们访问数组或容器与索引。

1。

C/c++数组和c++ STL容器从0到(size - 1)计算索引。

考虑一下,当您通过一对指针或迭代器之间的差值来计算数组或容器的大小时会发生什么:

size = ending_pointer - beginning_pointer;
// or
size = std::distance(beginning_iterator, ending_iterator);

如果ending_pointer或ending_iterator指的是最后一根后的位置,这将会给你一个大小。

2。

按照惯例,循环遍历一个索引如下的数组:

for (size_t i = 0 ; i < size ; ++i)

注意,我们使用小于而不是小于或等于。

为迭代器:

// Usually found in implementations of STL algorithms
for (iterator_type it = beginning_iterator ; it != ending_iterator ; ++it)

注意我们用not-equal-to代替equal-to。

3。

要处理大小为0的数组或容器的情况,在最后一个位置后面一个位置的惯例对于程序员和编译器优化是方便的,因为开始迭代器将等于结束迭代器。

for (iterator_type it = beginning_iterator ; it != ending_iterator_which_equals_the_beginning ; ++it)
// still consistent to
for (size_t i = 0 ; i < size_which_is_zero ; ++i)

在其他人的回答中提到,我们更喜欢std::begin(container)std::end(container)。它们可以将我们从指针算术的麻烦中解放出来,从而减少出错的可能性。它们还使泛型编码更容易。

std::accumulate(...)通常与std::begin(...)std::end(...)函数一起使用更好。根据代码std::accumulate(first, last, 0),它总结了从firstlast的元素,但没有last。当您使用容器方法begin()时,end()代码汇总每个元素(如您所料),因为end()返回迭代器到past-the-end元素(最后一个真元素之后的元素)。例如,在循环中使用:

vector<int> vec = { 1, 2, 3, 4, 5, 6, 7, 8 };
for (auto it = begin(vec); it != end(vec); ++it)
{
    // do some work ...
}

函数std::begin(...)std::end(...)使用简单的c++数组。使用&a[3]的代码可以工作,因为您指向真正的最后一个元素之后的下一个元素。但是你的代码是不正确的,你不能这样写。