C ++重载谓词和值的std :: count_if()

时间:2016-10-10 08:47:01

标签: c++ templates c++11 overloading

考虑下面的代码,其中的目的是重载std::count_if()以使用容器作为参数而不是像往常一样使用输入和输出迭代器。

// 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;
}

结果是编译器错误,表示调用不明确。

有没有办法让这项工作?

3 个答案:

答案 0 :(得分:1)

如果您使用标准容器(value_type 1 ),您可以尝试:

// 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);
}

通过强制第二个参数的类型(不使其成为模板参数),可以避免歧义。但是,我可能不会这样做,并且会坚持count / count_if的标准版本。

1 如果您不能依赖Cont_T::value_type,则可以使用更“通用”decltype(*c.begin()))或类似内容替换它。 < / p>

答案 1 :(得分:1)

对于某些SFINAE,您可以

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{});
}

作为奖励,这也适用于C阵列。

Demo

答案 2 :(得分:0)

两个错误:你混淆了std :: count()和std :: count_if(),你错了。

首先,std :: count_if()需要一个谓词,而不是一个值。谓词是一个函数(或lambda表达式),返回一个布尔值是否应计算参数。你想要输入一个值,所以你需要使用std:count()代替。

其次,你不能只传递矢量。相反,您需要传递两个迭代器指定的范围。

请查看此页面以获取std :: count()和std :: count_if()的工作示例: http://en.cppreference.com/w/cpp/algorithm/count