如何正确地将comparator传递给另一个模板函数

How properly pass comparator to another template function

本文关键字:另一个 函数 正确地 comparator      更新时间:2023-10-16

我需要一个函数,它将接受两个迭代器和一个自定义比较器来比较这两个迭代器的值。我不想为此使用额外的模板参数。我是这样实现的:

template<typename T>
void foo(T begin, T end,
  function<bool(decltype(*begin), decltype(*begin))> compare = less<decltype(*begin)>())
{
    // I want to use 'compare' as: "compare(*begin, *begin);"
}

这段代码已经被clang正常编译,但在GCC 5.4.0上我有一些错误:

binary_heap.cpp:10:79: error: local variable 'begin' may not appear in this context
   function<bool(decltype(*begin), decltype(*begin))> compare = less<decltype(*begin)>())
                                                                               ^
binary_heap.cpp:10:85: error: template argument 1 is invalid
   function<bool(decltype(*begin), decltype(*begin))> compare = less<decltype(*begin)>())

如何纠正这段代码,使其可以成功地在clang和GCC上编译?或者也许有一个更合适的解决方案来定义这样的函数?

您不能在默认实参中使用形参,即使在未求值的上下文中也是如此(尽管可能应该修改标准以放宽该限制)。代替decltype(*begin),使用

decltype(*std::declval<T&>())

编辑:实际上,这可能不会做您想要的,因为如果应用*操作符产生左值,那么decltype说明符将解析为左值引用,但是您希望未引用类型作为std::less的参数。最好使用:

typename std::iterator_traits<T>::value_type

编辑2:我同意krzaq,最好只是添加另一个模板参数

仅仅通过强制使用function(更多细节在这里),就会大大降低算法的性能。只需要模板推导出Compare,你就可以了:

template<typename T, typename Compare = std::less<typename iterator_traits<T>::value_type>>
void foo(T begin, T end, Compare compare = {})
{
    // I want to use 'compare' as: "compare(*begin, *begin);"
}

有趣的是,标准算法通过按值而不是转发引用强制/建议使用轻量级函子。


要回答实际的问题:您可以使用std::less<>,它默认为std::less<void>,并模板化operator()进行实际的比较:

template<typename T>
void foo(T begin, T end,
  function<bool(decltype(*begin), decltype(*begin))> compare = less<>())
{
    // I want to use 'compare' as: "compare(*begin, *begin);"
}

新增模板参数Compare。这使得编译器更容易内联。当我在它支持哨兵。

template<class It, class Sentinel, class Compare=std::less<void>>
void foo(It begin, Sentinel end, Compare cmp={}) {
}

但是,假设c++ 14,排除上述情况,我会这样做:

template<class T>
using comparator_sig = bool(T const&,T const&);
template<class T>
using comparator = std::function<comparator_sig<T>>;
template<class It>
using compare_inside_it = comparator<
  typename std::iterator_traits<It>::value_type
>;
template<typename T>
void foo(T begin, T end, compare_inside_it<T> compare = less<>) {
}
C++11中的

,我会写我自己的less<void>:

struct compare_less {
  template<class T>
  bool operator()( T const& lhs, T const& rhs ) const {
    return std::less<T>{}(lhs, rhs);
  }
};

和做的事:

template<typename T>
void foo(T begin, T end, compare_inside_it<T> compare = compare_less{}) {
}

或者直接做:

template<typename T>
void foo(T begin, T end, compare_inside_it<T> compare = {}) {
  if (!compare) compare=compare_less{};
}