有条件比较的优雅解决方案

What is elegant solution of conditional comparison?

本文关键字:解决方案 比较 有条件      更新时间:2023-10-16

我想基于标志的值以设计数/上升顺序排序数字向量。这样的东西:

int main()
{
    vector<int> numbers;
    bool is_negatif = false;
    // Do some stuff
    sort(numbers.begin(), numbers.end(), is_negatif ? less<int>(): greater<int>());
}

不幸的是,由于不同类型的较少/涂料结构,因此不会编译此类代码。

我正在使用这样的代码:

template<class _Ty>
struct MyComparator
{
   template <bool Less>
   static bool compare(const _Ty& _Left, const _Ty& _Right);
   template <>
   static bool compare<true>(const _Ty& _Left, const _Ty& _Right)
   {
      return _Left < _Right;
   }
   template <>
   static bool compare<false>(const _Ty& _Left, const _Ty& _Right)
   {
       return _Left > _Right;
   }
};

是用法:

sort(numbers.begin(), numbers.end(), is_negatif ? &MyComparator<int>::compare<true> : &MyComparator<int>::compare<false>);

这种问题的优雅解决方案是什么?

注意:bool值在编译时不知道!

如果您关心性能,那么对std::sort的单个呼叫将不会切割。我刚刚检查了godbolt.org确保:GCC和Clang生成的组件都将任一功能指针加载到寄存器中,然后用于std::sort中的每个比较。
std::function由于类型的擦除而更糟。

movl    bool compareLess<int>(int const&, int const&), %ebp
// ...
movl    bool compareGreater<int>(int const&, int const&), %eax
// ...
call   [std::__introsort_loop specialized for bool (*)(int const&, int const&)]

但是,如果您将std::sort的两个专业分开:

if(is_negatif)
    sort(numbers.begin(), numbers.end(), std::less<>{});
else
    sort(numbers.begin(), numbers.end(), std::greater<>{});

您最终得到了两个类型的呼叫,在其中编译器可以将比较内联。

call   [std::__introsort_loop specialized for std::less<void>]
// ...
call   [std::__introsort_loop specialized for std::greater<void>]

(疯狂的长期专业名称缩写为清晰)

我找到了另一个解决方案。这似乎适合我。当然,也许有更优雅的解决方案

bool is_negatif = false;
// Do some stuff
std::function<bool(int, int)> func = less<int>();
if (!is_negatif)
    func = greater<int>();
sort(numbers.begin(), numbers.end(), func);

您可以尝试使用类似的东西:

sort(numbers.begin(), numbers.end(), 
     is_negatif ? function<bool(int,int)>(less<int>()) :
                  greater<int>());

但是,这应该比普通的std::less<int>()比较器的性能要差得多,因为此比较器可能不会被隔离。

在体内使用is_negatif的比较器更易于直列,但它们会一次又一次地检查is_negatif

如果您想要出色的性能,请保持简单:

if (is_negatif)
    sort (numbers.begin(), numbers.end(), less<int>());
else
    sort (numbers.begin(), numbers.end(), greater<int>());
template <class T>
struct MyComparator
{
    MyComparator(bool __is_negatif)
    {
            m_is_negatif = __is_negatif;
    }
    bool operator()(const T& x, const T& y) const
    {
            if(m_is_negatif)
            {
                    return (x > y);
            }
            return (x < y);
    }
    private:
            bool m_is_negatif;
};

 vector<int> numbers;
 bool is_negatif = false;
 sort(numbers.begin(), numbers.end(), MyComparator<int>(is_negatif));

另一个解决方案就是将整个东西包裹在lambda中:

sort(numbers.begin(), numbers.end(), [&](int l, int r) {
  return is_negatif ? l < r : l > r;
});

如果您担心有条件地影响性能过多,那就不要。任何半个体面的分支预测器都将弄清楚在排序期间应始终调用哪些比较。

lambda怎么样?

怎么样
bool ascending = false;
auto sorting_condition = [&](const int & a, const int & b) -> bool {
    if (ascending) return a > b;
    else return !(a < b);
}
sort(numbers.begin(), numbers.end(), sorting_condition);