22

I am trying to combine std::accumulate with std::min. Something like this (won't compile):

vector<int> V{2,1,3};   
cout << accumulate(V.begin()+1, V.end(), V.front(), std::min<int>);

Is it possible? Is it possible to do without writing wrapper functor for std::min?
I know that I can do this with lambdas:

vector<int> V{2,1,3};   
cout << std::accumulate(
    V.begin()+1, V.end(),
    V.front(), 
    [](int a,int b){ return min(a,b);}
);

And I know there is std::min_element. I am not trying to find min element, I need to combine std::accumulate with std::min (or ::min) for my library which allows function-programming like expressions in C++.

Leonid Volnitsky
  • 8,854
  • 5
  • 38
  • 53

2 Answers2

25

The problem is that there are several overloads of the min function:

template <class T> const T& min(const T& a, const T& b);

template <class T, class BinaryPredicate>
const T& min(const T& a, const T& b, BinaryPredicate comp);

Therefore, your code is ambiguous, the compiler does not know which overload to choose. You can state which one you want by using an intermediate function pointer:

#include <algorithm>
#include <iostream>
#include <vector>

int main()
{
  std::vector<int> V{2,1,3};
  int const & (*min) (int const &, int const &) = std::min<int>;
  std::cout << std::accumulate(V.begin() + 1, V.end(), V.front(), min);
}
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • 2
    You could use an ugly cast too `(const int& (*)(const int&, const int&))std::min`. – Jesse Good Jul 24 '12 at 08:07
  • 4
    I tend to like the lambda version better. – moooeeeep Jul 24 '12 at 08:07
  • 4
    @JesseGood: y u no `static_cast`? :\ – user541686 Jul 24 '12 at 08:08
  • 1
    @Mehrdad: I thought it would make it too long, but now that I think about it, it does help readability. – Jesse Good Jul 24 '12 at 08:12
  • Does anyone know why this doesn't seem to be a problem for C++98 compiles, including `-std=gnu++98` for GCC where `-std=gnu++11` errors out? (Of course, you have to remove the new-style initialization of `V`). – Michael Burr Jul 24 '12 at 15:17
  • If you use a cast, I would suggest using a `typedef` for readability, at least: `typedef const int& (*min_func)(const int&, const int&);` or `using min_func = const int& (*)(const int&, const int&);` then you can use `std::accumulate(..., static_cast(std::min));`. I wonder if `std::function` would also work? `std::function min_func = std::min; std::accumulate(..., min_func);` – Remy Lebeau May 02 '18 at 02:29
  • 1
    @RemyLebeau Sure, `std::function` would work. It would incur the cost of creating a `std::function` along with the type erasure it performs though. – Luc Touraille May 04 '18 at 13:03
  • Unfortunately, this is unspecified (possibly ill-formed) behaviour, as you [can't take the address of most functions in `std`](https://timsong-cpp.github.io/cppwp/n4861/namespace.std#6) – Caleth Sep 15 '21 at 16:10
0

From C++20 you can use std::ranges::min

#include <algorithm>
#include <iostream>
#include <vector>
#include <numeric>
#include <climits>

int main() {
    std::vector<int> v{1,2,3,47,5};
    std::cout << std::accumulate(v.begin(), v.end(), INT_MAX, std::ranges::min) << std::endl; 
    std::cout << std::accumulate(v.begin(), v.end(), INT_MIN, std::ranges::max) << std::endl; 
}

1
47

Note that there is no std::ranges::accumulate, as it was not done for C++20.

NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277