为什么std :: accumulate的行为与标准数组一样?

时间:2014-07-31 23:26:43

标签: c++ arrays pointers c++11

我刚刚进入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的正确值。

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

5 个答案:

答案 0 :(得分:20)

C ++范围定义为[first, last),所有STL算法都是这样工作的。在这种情况下,std::accumulate总结迭代器定义的范围后面的所有元素,从first开始到last结束而不实际取消引用它。

因此,将其称为std::accumulate(a, a+3, 0)实际上是正确的,等同于使用std::accumulate(begin(a), end(a), 0)调用它。

另请注意,这不会违反“无指针分配数组之外的规则”规则,因为指针位于最后一个元素后面有一个特定的异常。

答案 1 :(得分:5)

与大多数STL算法一样,

std::accumulate将迭代器作为容器的最后一个元素。在这种情况下,该迭代器将是&a[3]。您可能希望使用std::begin()std::end(),因为它适用于所有容器,并且不易出错。此外,由于数组上的std::begin只是一个指针,因此您可以始终说出std::begin()+k之类的内容。所以,你不会失去任何灵活性。

答案 2 :(得分:2)

结束迭代器是最后一个元素的一个,因为"范围"在stl中半开。这意味着包含第一个元素,而不包括最后一个元素。

除此之外,我建议你使用c ++ 11开始和结束免费功能。它们也是普通c数组的重载。

答案 3 :(得分:2)

我不知道迭代器(包括原始指针和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)

请注意,我们使用的是less-than而不是less-than-or-equal-。

对于迭代器:

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

如其他人所述&#39;答案,我们更喜欢std::begin(container)std::end(container)。它们可以使我们摆脱指针运算的麻烦,从而减少错误。它们还使通用编码更容易。

答案 4 :(得分:1)

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

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]的代码有效,因为您指向真正的最后一个元素之后的下一个元素。但是你的代码不正确,你不能这样编码。