Функция С++ STL sort(), двоичный предикат

У меня есть код, который меня смущает:

   sort(data, data+count, greater<int>() );

это функция сортировки в стандартной библиотеке C. Мне трудно понять смысл третьего аргумента. Я прочитал, что он называется двоичным предикатом. Что это значит и как я могу сделать свой собственный такой предикат?

Ответ 1

Третий аргумент называется predicate. Вы можете представить предикат как функцию, которая принимает несколько аргументов и возвращает true или false.

Так, например, вот предикат, который говорит вам, является ли целое число нечетным:

bool isOdd(int n) {
    return n & 1;
}

Вышеприведенная функция принимает один аргумент, поэтому вы можете назвать его unary предикат. Если вместо этого потребовалось два аргумента, вы бы назвали его предикатом binary. Вот двоичный предикат, который говорит вам, если его первый аргумент больше второго:

bool isFirstGreater(int x, int y) {
    return x > y;
}

Предикаты обычно используются очень универсальными функциями, позволяющими вызывающей функции определять, как должна себя вести функция, написав свой собственный код (при использовании таким образом предикат является специализированной формой callback). Например, рассмотрим функцию sort, когда нужно отсортировать список целых чисел. Что, если мы хотим, чтобы он сортировал все нечетные числа перед всеми четными? Мы не хотим, чтобы каждый раз, когда мы хотим изменить порядок сортировки, мы вынуждены писать новую функцию сортировки, потому что механика (алгоритм) сортировки явно не связана со спецификой (в каком порядке мы хотим, чтобы она вид).

Итак, давайте sort наш собственный предикат, чтобы сделать его сортировкой в ​​обратном порядке:

// As per the documentation of sort, this needs to return true
// if x "goes before" y. So it ends up sorting in reverse.
bool isLarger(int x, int y) {
    return x > y;
}

Теперь это будет сортироваться в обратном порядке:

sort(data, data+count, isLarger);

Как это работает, sort внутренне сравнивает пары целых чисел, чтобы решить, какой из них должен идти перед другим. Для такой пары x и y она делает это, вызывая isLarger(x, y).

Итак, в этот момент вы знаете, что такое предикат, где вы можете его использовать, и как создать свой собственный. Но что означает greater<int>?

greater<T> - это двоичный предикат, который сообщает, что его первый аргумент больше второго. Это также шаблонный struct, что означает, что он имеет много разных форм, основанных на типе его аргументов. Этот тип необходимо указать, поэтому greater<int> - это специализированная специализация по типу int (читайте больше на шаблонах С++, если вы считаете нужным).

Итак, если greater<T> является struct, как это может быть и предикат? Разве мы не говорим, что предикаты являются функциями?

Ну, greater<T> - это функция в том смысле, что она вызываема: она определяет оператор bool operator()(const T& x, const T& y) const;, что делает запись этого права:

std::greater<int> predicate;
bool isGreater = predicate(1, 2); // isGreater == false

Объекты типа класса (или struct s, которые почти одинаковы в С++), которые вызываются, называются объектами функций или функторы.

Ответ 2

Существует шаблон класса greater, которому нужен аргумент типа. Таким образом, вы предоставляете int как один. Он стал greater<int>, и вы создаете экземпляр этого класса и передаете его функции в качестве третьего аргумента.

Такой объект называется функциональным объектом или просто функтором в С++, поскольку класс перегружает (). Это вызываемый объект. Это примерно так:

template<typename T>
struct greater
{
   bool operator()(const T &a, const T &b)
   {
      //compare a and b and return either true or false.
      return a > b;
   }
};

Если вы создаете экземпляр greater<int> и, скажем, объект g, вы можете написать g(100,200), который вычисляет логическое значение, так как выражение g(100,200) вызывает operator(), передавая 100 в качестве первого аргумента и 200 в качестве второго аргумента, а operator() сравнивает их и возвращает либо true, либо false.

       std::cout << g(100,200) << std::endl;
       std::cout << g(200,100) << std::endl;

Вывод:

0
1

Демо-версия онлайн: http://ideone.com/1HKfC

Ответ 3

Бинарный предикат - это любая функция/объект, который получает два объекта (следовательно, двоичный) и возвращает bool (следовательно predicate); идея состоит в том, что он оценивает, удовлетворяют ли два объекта некоторым конкретным условиям - в примере, если один больше другого.

Вы можете создать предикат либо путем простого определения функции с правильной подписью

bool IsIntGreater(int First, int Second)
{
    return First>Second;
}

и передать имя функции в качестве аргумента (это приведет к передаче указателя функции) или созданию объекта функции (функтора), то есть объекта, который перегружает оператор вызова функции и, следовательно, может быть использован как функция; тип std::greater<T> - это шаблонный функтор, а в вашем фрагменте создается временный объект типа std::greater<int> и передается алгоритму std::sort.

Функторы имеют несколько преимуществ перед функциями, особенно когда они должны передаваться в качестве аргументов, посмотрите здесь для получения дополнительной информации об этом.