Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Sum an array in a lambda.

44 views
Skip to first unread message

Joseph Hesse

unread,
Dec 20, 2022, 12:00:49 PM12/20/22
to
I want to sum an array of int's in a lambda function.

In the following code, function f1 does this with no problem.

In function f2, I am able to sum an int array using a range based
for loop. That this works surprises me since the array name is not
converted to a pointer and the for loop "looks around" to find the
size of int x[].

The commented out code in f2 was my attempt, as in f1, to
put the code to sum the array in a lambda. It does not compile.

Is it possible to make this work?

Thank you,
Joe
=======================================================
#include <iostream>
#include <vector>
using namespace std;

void f1(){
vector<int> v = {1, 2, 3, 4};

auto fp = [] (vector<int> vi)
{
int sum = 0;
for(const int &i : vi)
sum += i;
return sum;
};

cout << "sum = " << fp(v) << '\n';
}

void f2(){
int x[4] = {1, 2, 3, 4};

int sum = 0;
for(const int &i : x)
sum += i;
cout << "sum = " << sum << '\n';

/*
auto fp = [] (int x[])
{
int sum = 0;
for(const int &i : x)
sum += i;
return sum;
};

cout << "sum = " << fp(x) << '\n';
*/
}

int main(){
f1();
f2();
return 0;
}

Öö Tiib

unread,
Dec 20, 2022, 12:30:03 PM12/20/22
to
On Tuesday, 20 December 2022 at 19:00:49 UTC+2, Joseph Hesse wrote:
> I want to sum an array of int's in a lambda function.
>
> In the following code, function f1 does this with no problem.
>
> In function f2, I am able to sum an int array using a range based
> for loop. That this works surprises me since the array name is not
> converted to a pointer and the for loop "looks around" to find the
> size of int x[].
>
> The commented out code in f2 was my attempt, as in f1, to
> put the code to sum the array in a lambda. It does not compile.

The ...

auto fp = [] (int x[])

... is by language rules equivalent to ...

auto fp = [] (int *x)

... so array length information is lost and range
based for has no idea what range you mean.

>
> Is it possible to make this work?

Sure, you should either use template ...

auto fp = []<size_t N>(int (&x)[N])

... or you should have fixed array reference ...

auto fp = [](int (&x)[4])

... then the range based for is happy with it.


Paavo Helde

unread,
Dec 20, 2022, 1:04:11 PM12/20/22
to
20.12.2022 19:00 Joseph Hesse kirjutas:
> I want to sum an array of int's in a lambda function.
>
> In the following code, function f1 does this with no problem.
>
> In function f2, I am able to sum an int array using a range based
> for loop.  That this works surprises me since the array name is not
> converted to a pointer and the for loop "looks around" to find the
> size of int x[].
>
> The commented out code in f2 was my attempt, as in f1, to
> put the code to sum the array in a lambda.  It does not compile.
>
> Is it possible to make this work?
>
> Thank you,
> Joe
> =======================================================
> #include <iostream>
> #include <vector>
> using namespace std;

>
> void f2(){
>   int x[4] = {1, 2, 3, 4};
>
>   int sum = 0;
>   for(const int &i : x)
>       sum += i;
>   cout << "sum = " << sum << '\n';
>
> /*
>   auto fp = [] (int x[])
>   {
>     int sum = 0;
>     for(const int &i : x)
>       sum += i;
>     return sum;
>   };
>
>   cout << "sum = " << fp(x) << '\n';
> */
> }

You can fix it easily by over-using auto:

void f2() {
int x[4] = { 1, 2, 3, 4 };

auto fp = [] (const auto& x)
{
int sum = 0;
for(const int &i : x)
sum += i;
return sum;
};

std::cout << "sum = " << fp(x) << '\n';

}


However, using C arrays seems fragile in general as they decay to
pointers too easily. This seems better:

void f2() {
std::array<int, 4> x = { 1, 2, 3, 4 };

auto fp = [] (const auto& range)
{
int sum = 0;
for(const int &i : range)
sum += i;
return sum;
};

std::cout << "sum = " << fp(x) << '\n';

}








Juha Nieminen

unread,
Dec 21, 2022, 1:35:36 AM12/21/22
to
Joseph Hesse <jo...@gmail.com> wrote:
> auto fp = [] (vector<int> vi)

By the way, you should almost never take class instances like this
as function parameters by value. You should almost always take them
as const reference.

When you take it by value like that, it will be *copied* for the
function. The larger the vector is, the heavier it becomes to copy.
In most cases (like here) the function doesn't need a copy of the
vector. A reference to the original suffices (and passing a reference
as parameter to the function is infinitely more efficient than
copying the entire vector).

The only case where you want to pass such an object by value instead
of by reference is when the function actually needs a deep-copy of
the object, but that's quite rare. (Even in such cases it's actually
usually better to still take the parameter by const reference and
copy it inside the function.)

Joseph Hesse

unread,
Dec 21, 2022, 11:56:47 AM12/21/22
to
On 12/20/22 11:29, Öö Tiib wrote:
> auto fp = [] (int x[])
>
> ... is by language rules equivalent to ...
>
> auto fp = [] (int *x)
>
> ... so array length information is lost and range
> based for has no idea what range you mean.
>
>>
>
In the following program, the array definition and range based for loop
are in the same scope so it appears that the range based for loop sees
the size of the array. The following program compiles with the gnu
compiler and runs correctly. I am surprised that it works.
=========================================
#include <iostream>
#include <vector>
using namespace std;
int main(){
int x[4] = {1, 2, 3, 4};
int sum = 0;
for(const int &i : x)
sum += i;
cout << "sum = " << sum << '\n';
return 0;
}

Keith Thompson

unread,
Dec 21, 2022, 1:32:26 PM12/21/22
to
Why are you surprised?

In `auto fp = [] (int x[])`, x is an array parameter, which is treated
as a pointer parameter. That equivalence applies only to function
parameters. In your example, you just have an array object.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for XCOM Labs
void Void(void) { Void(); } /* The recursive call of the void */

Joseph Hesse

unread,
Dec 22, 2022, 1:05:59 AM12/22/22
to
In the above program the x in the for loop is treated as
an int *. The fact that the program works means that the
for loop knows how far to increment the pointer to calculate
the sum. This is what surprises me, I thought the only information
a built in array type contains is a pointer to the first element.

Thank you,
Joe

Öö Tiib

unread,
Dec 22, 2022, 3:00:11 AM12/22/22
to
That is not true. The x in that program is int array of 4 elements,
not pointer. The array decays to pointer in lot of contexts but
range based for is not one of those. It treats x as an array.
<https://en.cppreference.com/w/cpp/language/range-for>


Ben Bacarisse

unread,
Dec 22, 2022, 7:43:00 AM12/22/22
to
You are right (of course!) but it's by no means trivial to follow the
details. The cppreference page gives an example, but it's explanation
is very hard to plough through for someone learning C++. You'd have to
understand how

auto&& __range = x;

works and what __range and __range + 4 mean following a declaration like
that.

Clearly (at least I think so) __range will be an rvalue reference to an
array of 4 ints, but when I try to ditch the auto with

int (&&__range)[4] = x;

g++ complains that it "cannot bind rvalue reference of type ‘int
(&&)[4]’ to lvalue of type ‘int [4]’. What is the type that is being
deduced for __range in

auto&& __range = x;

? (Using a lvalue reference works but that's not what the standard says
is going on with this range-based for statement.)

--
Ben.

Bonita Montero

unread,
Dec 22, 2022, 1:54:53 PM12/22/22
to
Am 20.12.2022 um 18:00 schrieb Joseph Hesse:

>   cout << "sum = " << fp(v) << '\n';

cout << "sum = " << accumulate( v.cbegin(), v.cend() ) << endl;


0 new messages