简体   繁体   中英

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?

If you are using standard containers (with value_type 1 ), 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.

With some SFINAE, you may do

namespace helper
{

    using std::begin;
    using std::end;

    struct low_priority {};
    struct high_priority : low_priority {};

    template<typename Cont, typename Pred>
    decltype(true == std::declval<Pred>()(*begin(std::declval<const Cont&>())),
             void(), std::size_t{})
    count_if(const Cont& c, Pred&& p, high_priority)
    {
        return std::count_if(begin(c), end(c), std::forward<Pred>(p));
    }

    template<typename Cont, typename T>
    decltype(*begin(std::declval<const Cont&>()) == std::declval<T>(),
             void(), std::size_t{})
    count_if(const Cont& c, T&& val, low_priority) {
        return std::count(begin(c), end(c), std::forward<T>(val));
    }

}

template <typename Cont, typename T>
std::size_t count_if(const Cont& c, T&& val_or_pred)
{
    return helper::count_if(c, std::forward<T>(val_or_pred), helper::high_priority{});
}

As bonus, that also works for C-arrays.

Demo

Two mistakes: You are mixing up std::count() and std::count_if(), and you are using it wrong.

First, std::count_if() expects a predicate, not a value. The predicate is a function (or lambda expression) returning a boolean whether the argument should be counted. You want to put in a value, so you need to use std:count() instead.

Secondly, you cannot just pass the vector. Instead you need to pass a range specified by two iterators.

Please checkout this page for a working example of both std::count() and std::count_if(): http://en.cppreference.com/w/cpp/algorithm/count

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM