为什么标准数组中的std::accumulate会有这样的行为? 我刚刚进入C++,我想我对指针有一个处理,但是 STD::累加器()/Cuff>让我困惑。< /P>

为什么标准数组中的std::accumulate会有这样的行为? 我刚刚进入C++,我想我对指针有一个处理,但是 STD::累加器()/Cuff>让我困惑。< /P>,c++,arrays,pointers,c++11,C++,Arrays,Pointers,C++11,给定阵列: int a[3] = { 5, 6, 7 }; 我想用std::accumulate()对数组的值求和,所以我给它传递一个指针,指向第一个元素,然后是最后一个元素,然后是累加器的起始值 std::accumulate(a, a + 2, 0); std::accumulate(&a[0], &a[2], 0); Oops:其中任何一个只返回前两个元素的总和:11 另一方面,如果第二个参数是一个无意义的指针,则超出范围 std::accumulate(a, a +

给定阵列:

int a[3] = { 5, 6, 7 };
我想用
std::accumulate()
对数组的值求和,所以我给它传递一个指针,指向第一个元素,然后是最后一个元素,然后是累加器的起始值

std::accumulate(a, a + 2, 0);
std::accumulate(&a[0], &a[2], 0);
Oops:其中任何一个只返回前两个元素的总和:
11

另一方面,如果第二个参数是一个无意义的指针,则超出范围

std::accumulate(a, a + 3, 0);
std::accumulate(&a[0], &a[3], 0);
。。。返回正确的
18


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

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

因此,像
std::acculate(a,a+3,0)
那样调用它实际上是正确的,等于用
std::acculate(begin(a),end(a),0)
调用它


还要注意的是,这并不违反“没有指向已分配数组外部的指针”规则,因为指向最后一个元素后面的指针有一个特殊的例外。

std::acculate
,与大多数STL算法一样,将迭代器带过容器的最后一个元素。在这种情况下,迭代器将是
&a[3]
。您可能需要使用
std::begin()
std::end()
,因为它适用于所有容器,并且不太容易出错。此外,由于数组上的
std::begin
只是一个指针,所以您可以始终说
std::begin()+k
。因此,您不会失去任何灵活性。

std::accumultium(…)
通常最好与
std::begin(…)
std::end(…)
函数一起使用。根据代码
std::acculate(first,last,0)
它将元素从
first
汇总到
last
,但没有
last
一个。当您使用容器方法
begin()
end()
code对每个元素求和(正如您所期望的那样),因为
end()
返回迭代器以超过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 ...
}
向量向量向量={1,2,3,4,5,6,7,8}; 对于(自动it=开始(vec);it!=结束(vec);+it) { //做一些工作。。。 }

函数<代码> STD::开始(…)< /COD>和<代码> STD::结束(…)/Cuff>用简单的C++数组工作。


使用
&a[3]
的代码之所以有效,是因为您指向了最后一个元素之后的下一个元素。但是您的代码不正确,您不能这样编码。

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


除此之外,我建议您使用c++11 begin和end free函数。它们对于普通c数组也是重载的。

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

一,

C/C++数组和C++ STL容器将索引从0到(大小为1)。 考虑在计算数组或容器的大小时,通过计算指针或迭代器对之间的差值会发生什么,如:

size = ending_pointer - beginning_pointer;
// or
size = std::distance(beginning_iterator, ending_iterator);
如果ending_指针或ending_迭代器指的是超过最后一个的位置,那么这将真正为您提供大小

二,

按照惯例,循环通过具有如下索引的数组:

for (size_t i = 0 ; i < size ; ++i)
请注意,我们使用“不等于”而不是“等于”

三,

为了处理大小为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)
for(iterator\u type it=beging\u iterator;it!=end\u iterator\u,它等于\u beging;++it)
//仍然符合
for(size_t i=0;i


正如其他人的回答中提到的,我们更喜欢
std::begin(container)
std::end(container)
。它们可以让我们摆脱指针运算的麻烦,从而减少出错的可能性。它们还使通用编码更容易。

关于为什么半开范围是最佳范围。
std::accumulate
的第二个参数是不包括在累加中的第一个元素的迭代器位置。再简单不过了。
&a[3]
是未定义的行为。
a+3
不是。@TemplateRex该问题的首要答案没有正确确定UB的缺乏,他走到最后,然后挥手。仍然没有回答的是
和*(a+3)
将取消引用指针
a+3
,如果取消引用,则将其视为取消引用
a+3
。无论这种情况是否为UB,使用它仍然是一种不好的模式,因为如果
a
是类类型(例如
std::vector
),它肯定可以是UB这对于处理退化情况是必要的,在这种情况下,您希望对零元素进行操作。对于刚好超出最后一个元素的指针,有一个特殊的异常,但是对于刚好位于第一个元素前面的指针,没有这样的异常。或者更简单地说,
a+3
,“因为数组上的std::begin()是一个随机访问迭代器,所以您总是可以说std::begin()+k”这样的话think@gigabytes,该标准对数组上的
std::begin
给出了以下声明:
template T*begin(T(&array)[N]);
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)