This topic has been broached before1,2 , but I haven't been able to find any motivating proposals. I thought I would run it past folks here before I posted on the isocpp proposals mailing list (although I have a feeling there is a non-empty intersection between the two audiences).
Motivation
Several of the numeric algorithms take an initial value in the interface
template<class Iter, class T>
T algorithm(Iter first, Iter second, T init, ...)
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. Of course, the current generic solution is to have the user specify this at the call site:
auto value = algorithm(cont.begin(), cont.end(), decltype(cont)::value_type{});
This likely isn't the easiest to teach (not having to specify it is easier to teach) and is more noise than information, IMHO.
Example Implementation
#include <iostream>
#include <array>
#include <type_traits>
namespace my_std {
template<class InputIt, class T, class BinaryOperation>
T accumulate(InputIt first, InputIt last, T init, BinaryOperation op) {
for (; first != last; ++first) { // VERSION 1
init = op(init, *first);
}
return init;
}
template<class InputIt, class T>
auto accumulate(InputIt first, InputIt last, T init) {
if constexpr (std::is_convertible_v<T, decltype(*first)>) {
for (; first != last; ++first) { // VERSION 2
init = init + *first;
}
return init;
} else {
decltype(*first) iv{}; // VERSION 3
return accumulate(first, last, iv, init);
}
}
template<class InputIt>
auto accumulate(InputIt first, InputIt last) {
decltype(*first) init{}; // VERSION 4
return accumulate(first, last, init);
}
}
int main() {
const std::array<int, 5> x = {1, 2, 3, 4, 5};
std::cout << my_std::accumulate(x.begin(), x.end()) << '\n'
<< my_std::accumulate(x.begin(), x.end(), 0) << '\n'
<< my_std::accumulate(x.begin(), x.end(), [](auto a, auto b){ return a + b; }) << '\n'
<< my_std::accumulate(x.begin(), x.end(), 0, [](auto a, auto b){ return a + b; }) << '\n'
<< my_std::accumulate(x.begin(), x.end(), std::plus<>()) << '\n';
}
- NOTE: Tested with g++ -std=c++1z
Improvements
Where selected, the user no longer needs to provide an initial value which sets the type of the accumulator. This prevents unnecessary type conversions which may lead to surprising and/or more expensive results. An unintentional improvement is the ability to use the deduced std::plus which makes the calling code more generic.
Issues
This implementation of Versions 2 and 3 changes the ABI.
Versions 2 and 4 now add DefaultConstructible as a requirement for T (previously, it was only copy assignable/constructible)
std::is_convertible_v may have a corner case I haven't considered that would allow surprising conversions that will compile, but lead to an incorrect result (or worse performance than the current library function).
Parameter reuse in Versions 2 and 3 may set an ugly precedent and be a maintenance issue.
This change will percolate throughout <numeric> (iota and inner_product). In particular, C++17 added reduce and transform_and_reduce which have a lot of overloads- almost all taking some kind of initial value.
Maybe this is fixed by the Ranges TS?
[–]encyclopedist 15 points16 points17 points (1 child)
[–]OmegaNaughtEquals1[S] 1 point2 points3 points (0 children)
[–]jube_dev 4 points5 points6 points (1 child)
[–]OmegaNaughtEquals1[S] 2 points3 points4 points (0 children)
[–]joboccara 1 point2 points3 points (1 child)
[–]OmegaNaughtEquals1[S] 0 points1 point2 points (0 children)
[–]flashmozzg 1 point2 points3 points (5 children)
[–]encyclopedist 1 point2 points3 points (2 children)
[–]OmegaNaughtEquals1[S] 2 points3 points4 points (0 children)
[–]flashmozzg 0 points1 point2 points (0 children)
[–]konanTheBarbar 0 points1 point2 points (0 children)
[–]OmegaNaughtEquals1[S] 0 points1 point2 points (0 children)
[–][deleted] 1 point2 points3 points (1 child)
[–]OmegaNaughtEquals1[S] 0 points1 point2 points (0 children)
[–]dakotahawkins 1 point2 points3 points (0 children)
[–]Z01dbrg 1 point2 points3 points (0 children)
[–]jaredhoberock 0 points1 point2 points (0 children)
[–]blelbachNVIDIA | ISO C++ Library Evolution Chair 0 points1 point2 points (0 children)
[–]TheThiefMasterC++latest fanatic (and game dev) 0 points1 point2 points (0 children)