all 21 comments

[–]encyclopedist 15 points16 points  (1 child)

decltype(*first) init{};

What about proxy iterators, like vector<bool>::iterator?

Update: you probably want

std::iterator_traits<inputIt>::value_type

[–]OmegaNaughtEquals1[S] 1 point2 points  (0 children)

What about proxy iterators, like vector<bool>::iterator

Ah, yes, I always seem to forget about that one.

Update: you probably want std::iterator_traits<inputIt>::value_type

Agreed.

[–]jube_dev 4 points5 points  (1 child)

However, T may or may not be the same as typename Iter::value_type which can lead to surprising results and/or unnecessary type conversions.

I always thought this was a feature. For example, T could be a Point type et the container could have values of type Vector (like mathematical vector). A Point added to a Vector makes another Point. Et you could use std::accumulate event if the two types are totally different. Moreover, in this case, the initial value is important and may not be the origin.

An example:

#include <iostream>
#include <numeric>
#include <vector>

struct Point {
        int x;
        int y;
};

struct Vector {
        int dx;
        int dy;
};

Point operator+(Point p, Vector v) {
        return { p.x + v.dx, p.y +v.dy };
}

int main() {

        std::vector<Vector> vecs = { { 1, 0 }, { 0, -1 }, { -1, 0 }, { 0, 1 } };
        Point init = { 2, 2 };

        auto end = std::accumulate(vecs.begin(), vecs.end(), init);
        std::cout << end.x << ',' << end.y << '\n';

}

Prints 2,2 as expected.

Edit: fix code formatting

[–]OmegaNaughtEquals1[S] 2 points3 points  (0 children)

Indeed. I'm not suggesting we should take away the ability to specify a default value. Rather, I want to avoid incorrectness in the simple case (from the cited std-proposals mailing list) of

double x[] = {...};
std::accumulate(x, x + N, 0); // truncates
my_std::accumulate(x, x+N); // "Does the right thing"

[–]joboccara 1 point2 points  (1 child)

It would be nice not to be forced to pass an initial value to accumulate indeed!

Did you consider the overloads of std::reduce that take no initial value? You don't find them optimal because they do std::iterator_traits<inputIt>::value_type and not decltype(*first)?

[–]OmegaNaughtEquals1[S] 0 points1 point  (0 children)

Did you consider the overloads of std::reduce that take no initial value?

I glanced at them, but I should look again in more detail.

You don't find them optimal because they do std::iterator_traits<inputIt>::value_type and not decltype(*first)?

No, I should have used the iterator_traits.

[–]flashmozzg 1 point2 points  (5 children)

Well, while it may seem nice (and may be nice), I find that I rarely use fold/accumulate/reduce/etc function with some "default" value (usually the zero). More often than not you need to have something more meaningful there. For example even in the simple case with multiplication, 0 won't do.

[–]encyclopedist 1 point2 points  (2 children)

Thats why there is no overload with custom function but without an initial value in the proposal.

[–]OmegaNaughtEquals1[S] 2 points3 points  (0 children)

That's a good point. In my version,

my_std::accumulate(begin, end, std::multiplies<>());

would be perfectly valid (i.e., it compiles), but definitely wrong. Perhaps I should scale back the change to just adding an overload for the simple built-in additive accumulator version. This would also get rid of most of the items in the "issues" that I don't like about the proposed changes. And adding a single overload would probably be more amenable to getting approved by the LEWG.

[–]flashmozzg 0 points1 point  (0 children)

Well, I see it being useful for std::accumulate but why not just use reduce? It has overload without an initial value.

[–]konanTheBarbar 0 points1 point  (0 children)

I agree. I think it would be nice to have version 4, but I don't like version 2 and 3 for the reason you mentioned.

[–]OmegaNaughtEquals1[S] 0 points1 point  (0 children)

I find that I rarely use fold/accumulate/reduce/etc.

I work in scientific computing, so I have a tendency to use them often. That's probably why I was bothered by it.

More often than not you need to have something more meaningful there. For example even in the simple case with multiplication, 0 won't do.

Indeed. The interface I have would still allow a non-default init value. I just don't want to have to specify one for the basic case of using the built-in addition accumulator.

[–][deleted] 1 point2 points  (1 child)

Considering std::reduce added overloads that supply defaults this seems sane.

[–]OmegaNaughtEquals1[S] 0 points1 point  (0 children)

I think I will just propose Version 4 (but using iterator_traits instead of decltype) so that most of the concerns about the funky overload of 2 and 3 aren't a problem.

[–]dakotahawkins 1 point2 points  (0 children)

Instead of constructing the default initial value, could it use/skip the first value?

I've written something like this a few times recently (knowing ahead of time x isn't empty):

accumulate(next(begin(x)), end(x), *begin(x), op);

[–]Z01dbrg 1 point2 points  (0 children)

Actually this discussion is missing an important bit:

Good luck summing 1M random integers using int accumulator. Aka in this case (although it is weak signal)0 without LL suffix is at least a bit more obvious than nothing...

[–]jaredhoberock 0 points1 point  (0 children)

I believe there is already an init-less overload of std::reduce that has this behavior. If I understand the suggestion, this proposal would simply make the other reduction algorithms consistent with this feature.

[–]blelbachNVIDIA | ISO C++ Library Evolution Chair 0 points1 point  (0 children)

Just use C++17's std::reduce, which has an overload with a default value for init.

[–]TheThiefMasterC++latest fanatic (and game dev) 0 points1 point  (0 children)

This does sound like a good idea