2016-12-29 6 views
1

Я хочу сортировать вектор чисел в порядке desend/ascend, основываясь на значении флага. Что-то вроде этого:Что такое элегантное решение условного сравнения?

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

К сожалению, такой код не будет компилироваться из-за различных типов меньше/greaters структур.

Я использую такой код:

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 не известно во время компиляции!

+0

'станд :: сортировки()' сортирует в порядке возрастания по умолчанию (с помощью 'станд :: less'). Почему бы не использовать это, а затем использовать 'std :: reverse()', если требуется порядок по убыванию? – Peter

+0

Это приведет к дополнительной производительности – LmTinyToon

+0

Зачем вам нужен тройной оператор? Просто передайте 'is_negatif' в качестве параметра шаблона. – SingerOfTheFall

ответ

2

Я нашел другое решение. Мне кажется подходящим для меня. Конечно, может быть, есть более элегантное решение

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); 
1
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)); 
1

Другим решением является просто обернуть все это в лямбда:

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

Если вы беспокоитесь об условном, влияющие на работу слишком много , не надо. Любой полупристойный предсказатель отрасли будет определять, какое сравнение следует всегда вызывать во время сортировки.

+1

вы избили меня на 24 секунды, вот голосование –

0

Как насчет лямбда?

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); 
+0

'! (a > b) 'эквивалентно' a <= b'. Это сравнение не представляет собой строгий слабый порядок, поэтому не соответствует требованиям 'std :: sort()'. – Peter

+0

Спасибо. Исправлено –

2

Вы можете попробовать использовать что-то вроде этого:

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>()); 
+0

Я вряд ли думаю, что эти циклы станут основным узким местом. – StoryTeller

+0

@StoryTeller OP хотел что-то, что будет быстрее, чем 'sort (...); if (is_negatif) reverse (...); ' –

+0

Я хотя бы один и тот же, используя простой' if (is_negatif) {less} else {больше} '. Но запутался в различии между тем, что такое элегантность кода и что такое читаемость кода. – sameerkn

3

Если вы заботитесь о производительности, то один вызов 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<>{}); 

Вы в конечном итоге с двумя типа Осведомленный вызовов, в которых компилятор может затем INLINE сравнение.

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

(Сумасшедшие длинные специализаций имена сокращенные для ясности)

Смежные вопросы