Kemal Kemal - 1 month ago 9
C++ Question

C++ overloading std::count_if() for a predicate and a value

Consider the code below, where the intention is to overload

std::count_if()
to work with a container as an argument instead of input and output iterators as usual.

// overload for call with predicate
template<typename Cont_T, typename Pred_T>
typename std::iterator_traits<typename Cont_T::iterator>::difference_type
count_if(const Cont_T& c, Pred_T p) {
return std::count_if(c.begin(), c.end(), p);
}

// overload for call with value
template<typename Cont_T, typename T = typename Cont_T::value_type>
typename std::iterator_traits<typename Cont_T::iterator>::difference_type
count_if(const Cont_T& c, const T& val) {
return std::count_if(c.begin(), c.end(), val);
}

int main() {
using namespace std;

vector<int> v{1,2,3};
count_if(v, 2); // ambiguous call

return 0;
}


The result is a compiler error that says the call is ambiguous.

Is there a way to make this work?

Answer

If you are using standard containers (with value_type1), you could try:

// overload for call with predicate
template<typename Cont_T>
typename std::iterator_traits<typename Cont_T::iterator>::difference_type 
count_if(const Cont_T& c, std::function<bool(typename Cont_T::value_type)> p) {
    return std::count_if(c.begin(), c.end(), p);
}

// overload for call with value
template<typename Cont_T>
typename std::iterator_traits<typename Cont_T::iterator>::difference_type
count_if(const Cont_T& c, const typename Cont_T::value_type& val) {
    return std::count(c.begin(), c.end(), val);
}

By forcing the type of the second parameter (not making it a template parameter), you avoid ambiguity. However, I would probably not do that and would stick to the standard version which is count / count_if.

1 If you cannot rely on Cont_T::value_type, you could replace it by a more "general" decltype(*c.begin())) or something alike.