Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I pipe to range-v3 accumulate?

I found older questions from 3y ago that say that in general it is not possible, but I would really like to pipe to accumulate since it is quite nice in some cases, for example this:

const double val = data | transform(...) | accumulate (...);

So I wonder if something has been added to range-v3/C++20 ranges that enables me to do this.

like image 510
NoSenseEtAl Avatar asked Dec 01 '19 22:12

NoSenseEtAl


People also ask

Is range v3 header only?

Disclaimers. The library used in the code examples is not really the C++20 ranges, it's the ranges-v3 open-source library from Eric Niebler, which is the basis of the proposal to add ranges to the C++. It's a header-only library compatible with C++11/14/17.

What is Range v3?

Range v3 is a generic library that augments the existing standard library with facilities for working with ranges. A range can be loosely thought of a pair of iterators, although they need not be implemented that way.

What are C++ ranges?

Range - Ranges are an abstraction that allows a C++ program to operate on elements of data structures uniformly. On minimum a range contains defines begin() and end() to elements. There are several different types of ranges: containers, views, sized ranges, Container - It's a range that owns the elements.


Video Answer


1 Answers

No.

The only things you can pipe into are range adapters -- algorithms that take in a range and produce a range. Algorithms that take in a range and return a single object (also known as catamorphisms) are not pipeable in range-v3 or C++20 ranges.

You have to write it this way:

const double val = accumulate(data | transform(...));

As to why accumulate and similar algorithms will struggle to ever be |-able. Consider that we want algo(rng, x) and rng | algo(x) to mean the same thing. Further, consider that the "total call" algo(rng, x) can be fully constrained (since you have all the information) while the "partial call" algo(x) basically has to be completely unconstrained in all but rare circumstances... basically roughly taking auto&&...

The problem is we necessarily run into ambiguities when the second argument, x, can also be a range. How do you distinguish between the intent being a total call or a partial call?

Here's an example using string:

accumulate("hello"s, ""s)

This is a total call, that uses the default binary operator + - which is string concatentation. What this does is iterate over the elements of the range of chars and add them one by one to the initial empty string. This is an inefficient, yet correct, way to copy a string. You end up with the value "hello"s.

What about its equivalent pipe version?

"hello"s | accumulate(""s)

What does the right-hand side mean? Can accumulate(""s) be considered a total call? Yes it can! The defaulted 2nd argument would be char() and the defaulted third argument would be plus(), this works fine, and so the value of accumulate(""s) is the integer 0 - making the whole expression ill-formed because there's no operator|(string, int).

How do you make this work with accumulate?

like image 157
Barry Avatar answered Oct 22 '22 23:10

Barry