I try to implement that summing up all elements of a vector<vector<int>>
in a non-loop ways.
I have checked some relevant questions before, How to sum up elements of a C++ vector?.
So I try to use std::accumulate
to implement it but I find it is hard for me to overload a Binary Operator
in std::accumulate
and implement it.
So I am confused about how to implement it with std::accumulate
or is there a better way?
If not mind could anyone help me?
Thanks in advance.

- 1,635
- 4
- 21
- 39
-
4`I find it is hard for me to overload a Binary Operator in std::accumulate and implement it.` Why? What have you tried and why didn't it work? – tkausl Nov 03 '19 at 14:29
-
4You can't do it without a loop. `std::accumulate` has a loop inside it, and by using the algorithm you're just using a loop that was already coded for you. – Spencer Nov 03 '19 at 14:33
-
3Don't overload things in the `std` You also need to show some work. This isn't a tutorial site. You need to ask specific questions about work you've tried. – doug Nov 03 '19 at 14:34
-
1You don't overload anything, you pass a binary operation which you create down to std::accumulate. It could be a function, a lambda, or a functional object. – n. m. could be an AI Nov 03 '19 at 14:59
-
No loops? Why not? Well, whatever the rationale... then can you use `goto`? – Eljay Nov 03 '19 at 15:49
-
1The fundamental issue is that you need a loop or you have to add each item sequentially without a loop. The `std::accumulate` uses a loop. Even specialized processor instructions use a loop. So, why avoid loops? – Thomas Matthews Nov 03 '19 at 17:52
-
@ThomasMatthews same reason one would avoid explicit gotos. Loops are at a too low level of abstraction. – n. m. could be an AI Nov 05 '19 at 04:52
7 Answers
You need to use std::accumulate
twice, once for the outer vector
with a binary operator that knows how to sum the inner vector
using an additional call to std::accumulate
:
int sum = std::accumulate(
vec.begin(), vec.end(), // iterators for the outer vector
0, // initial value for summation - 0
[](int init, const std::vector<int>& intvec){ // binaryOp that sums a single vector<int>
return std::accumulate(
intvec.begin(), intvec.end(), // iterators for the inner vector
init); // current sum
// use the default binaryOp here
}
);

- 5,281
- 21
- 36
In this case, I do not suggest using std::accumulate
as it would greatly impair readability. Moreover, this function use loops internally, so you would not save anything. Just compare the following loop-based solution with the other answers that use std::accumulate
:
int result = 0 ;
for (auto const & subvector : your_vector)
for (int element : subvector)
result += element;
Does using a combination of iterators, STL functions, and lambda functions makes your code easier to understand and faster? For me, the answer is clear. Loops are not evil, especially for such simple application.

- 3,195
- 1
- 8
- 21
-
BTW, the question says non-loop ways. Your `for` statements are loops. – Thomas Matthews Nov 03 '19 at 17:54
-
@ThomasMatthews I wrote this answer to counter the obsession of using the STL library at all cost. I agree that it doesn't answer the question as it is, but I find it to be a better alternative than letting OP go through the path of code clutter. Furthermore, using `std::accumulate` is also not answering the question as it uses loops inside it. If we follow this logic, then defining a function `sum` that does the job with loops and then calling this function would also considered a valid answer. – Gilles-Philippe Paillé Nov 03 '19 at 18:11
-
@ThomasMatthews After reading your answer, I see that we both agree on the loop thing, just not on the way of answering ;) – Gilles-Philippe Paillé Nov 03 '19 at 18:25
According to https://en.cppreference.com/w/cpp/algorithm/accumulate , looks like BinaryOp has the current sum on the left hand, and the next range element on the right. So you should run std::accumulate on the right hand side argument, and then just sum it with left hand side argument and return the result. If you use C++14 or later,
auto binary_op = [&](auto cur_sum, const auto& el){
auto rhs_sum = std::accumulate(el.begin(), el.end(), 0);
return cur_sum + rhs_sum;
};
I didn't try to compile the code though :). If i messed up the order of arguments, just replace them.
Edit: wrong terminology - you don't overload BinaryOp, you just pass it.

- 578
- 3
- 6
-
Why is the lambda set to auto capture by reference when there's nothing to capture? – Shloim Nov 04 '19 at 07:32
-
Signature of std::accumulate
is:
T accumulate( InputIt first, InputIt last, T init,
BinaryOperation op );
Note that the return value is deduced from the init
parameter (it is not necessarily the value_type
of InputIt
).
The binary operation is:
Ret binary_op(const Type1 &a, const Type2 &b);
where... (from cppreference)...
The type
Type1
must be such that an object of typeT
can be implicitly converted toType1
. The typeType2
must be such that an object of typeInputIt
can be dereferenced and then implicitly converted toType2
. The typeRet
must be such that an object of typeT
can be assigned a value of typeRet
.
However, when T
is the value_type
of InputIt
, the above is simpler and you have:
using value_type = std::iterator_traits<InputIt>::value_type;
T binary_op(T,value_type&).
Your final result is supposed to be an int
, hence T
is int
. You need two calls two std::accumulate
, one for the outer vector (where value_type == std::vector<int>
) and one for the inner vectors (where value_type == int
):
#include <iostream>
#include <numeric>
#include <iterator>
#include <vector>
template <typename IT, typename T>
T accumulate2d(IT outer_begin, IT outer_end,const T& init){
using value_type = typename std::iterator_traits<IT>::value_type;
return std::accumulate( outer_begin,outer_end,init,
[](T accu,const value_type& inner){
return std::accumulate( inner.begin(),inner.end(),accu);
});
}
int main() {
std::vector<std::vector<int>> x{ {1,2} , {1,2,3} };
std::cout << accumulate2d(x.begin(),x.end(),0);
}

- 109,796
- 11
- 89
- 185
Solutions based on nesting std::accumulate
may be difficult to understand.
By using a 1D array of intermediate sums, the solution can be more straightforward (but possibly less efficient).
int main()
{
// create a unary operator for 'std::transform'
auto accumulate = []( vector<int> const & v ) -> int
{
return std::accumulate(v.begin(),v.end(),int{});
};
vector<vector<int>> data = {{1,2,3},{4,5},{6,7,8,9}}; // 2D array
vector<int> temp; // 1D array of intermediate sums
transform( data.begin(), data.end(), back_inserter(temp), accumulate );
int result = accumulate(temp);
cerr<<"result="<<result<<"\n";
}
The call to transform
accumulates each of the inner arrays to initialize the 1D temp
array.

- 51,587
- 17
- 154
- 173
-
In principle, using implicit loops rather than raw `for` gives you a standard way of specifying parallelism ([if your compiler is up to spec](https://stackoverflow.com/q/51031060/86967)). See: [Extensions for parallelism](https://en.cppreference.com/w/cpp/experimental/parallelism) – Brent Bradburn Nov 03 '19 at 23:08
To avoid loops, you'll have to specifically add each element:
std::vector<int> database = {1, 2, 3, 4};
int sum = 0;
int index = 0;
// Start the accumulation
sum = database[index++];
sum = database[index++];
sum = database[index++];
sum = database[index++];
There is no guarantee that std::accumulate
will be non-loop (no loops). If you need to avoid loops, then don't use it.
IMHO, there is nothing wrong with using loops: for
, while
or do-while
. Processors that have specialized instructions for summing arrays use loops. Loops are a convenient method for conserving code space. However, there may be times when loops want to be unrolled (for performance reasons). You can have a loop with expanded or unrolled content in it.

- 56,849
- 17
- 98
- 154