C++ 为谓词和值重载 std::count_if()
C++ overloading std::count_if() for a predicate and a value
考虑下面的代码,其目的是重载 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;
}
结果是编译器错误,指出调用不明确。
有没有办法让它工作?
两个错误:你混淆了 std::count() 和 std::count_if(),而且你用错了。
首先,std::count_if() 需要一个谓词,而不是一个值。谓词是一个函数(或 lambda 表达式)返回一个布尔值是否应该计算参数。你想输入一个值,所以你需要使用 std:count() 代替。
其次,你不能只传递向量。相反,您需要传递由两个迭代器指定的范围。
请查看此页面以获取 std::count() 和 std::count_if() 的工作示例:
http://en.cppreference.com/w/cpp/algorithm/count
如果您使用的是标准容器(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
,可以换成更"general"decltype(*c.begin())
)或类似的东西。
有了一些 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 数组。
考虑下面的代码,其目的是重载 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;
}
结果是编译器错误,指出调用不明确。
有没有办法让它工作?
两个错误:你混淆了 std::count() 和 std::count_if(),而且你用错了。
首先,std::count_if() 需要一个谓词,而不是一个值。谓词是一个函数(或 lambda 表达式)返回一个布尔值是否应该计算参数。你想输入一个值,所以你需要使用 std:count() 代替。
其次,你不能只传递向量。相反,您需要传递由两个迭代器指定的范围。
请查看此页面以获取 std::count() 和 std::count_if() 的工作示例: http://en.cppreference.com/w/cpp/algorithm/count
如果您使用的是标准容器(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
,可以换成更"general"decltype(*c.begin())
)或类似的东西。
有了一些 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 数组。