В чем разница между бинарным и унарным предикатами в C++ STL?

Ответ

Предикат в C++ — это вызываемый объект (функция, лямбда, функтор), возвращающий значение, конвертируемое в bool.

  • Унарный предикат принимает один аргумент.
  • Бинарный предикат принимает два аргумента.

Различие в количестве аргументов определяет, в каких алгоритмах STL их можно использовать.

Примеры использования:

Унарный предикат (например, в std::find_if, std::remove_if, std::all_of):

// Лямбда как унарный предикат: проверяет, является ли число четным.
auto isEven = [](int x) -> bool { return x % 2 == 0; };
std::vector<int> data = {1, 3, 2, 5, 4};
// find_if передает каждый элемент вектора в isEven по одному.
auto it = std::find_if(data.begin(), data.end(), isEven);
if (it != data.end()) {
    std::cout << "Первое четное число: " << *it << std::endl; // Выведет: 2
}

Бинарный предикат (например, в std::sort, std::lower_bound, std::set с пользовательским компаратором):

// Функтор как бинарный предикат: сравнивает строки по длине.
struct CompareByLength {
    bool operator()(const std::string& a, const std::string& b) const {
        return a.size() < b.size();
    }
};
std::vector<std::string> words = {"apple", "pie", "banana", "a"};
// sort передает пары элементов из вектора в компаратор.
std::sort(words.begin(), words.end(), CompareByLength());
// words теперь: ["a", "pie", "apple", "banana"]

Современный C++: Чаще всего в качестве предикатов используют лямбда-выражения прямо на месте вызова алгоритма, что делает код компактным и читаемым.

std::sort(words.begin(), words.end(),
          [](const auto& a, const auto& b) { return a.size() < b.size(); });

Ответ 18+ 🔞

А, предикаты! Ну, это ж классика, ёпта. Сейчас объясню на пальцах, чтобы даже мартышлюшка поняла.

Смотри, предикат в C++ — это просто такая штука, которую можно вызвать, и она тебе вернёт либо правду, либо ложь. Ну, bool, короче. Как твоя бывшая, когда говорит, что ты ей больше не звонил — либо правда, либо пиздёж.

Дальше всё просто, как три копейки:

  • Унарный предикат — это как сторож на проходной. Ему одного чела подсунул — он его проверил и сказал: "Проходи" (true) или "Иди нахуй" (false).
  • Бинарный предикат — это уже как два мужика в баре, которые друг на друга смотрят. Ему нужно два аргумента, чтобы сравнить и решить, кто круче. Типа "Вася лучше Пети" или "Эта строка короче той".

Вот смотри, как это в коде выглядит, бля:

Унарный предикат (используют, например, в find_if или remove_if):

// Лямбда — унарный предикат. Проверяет, чётное ли число. Один аргумент — и всё.
auto isEven = [](int x) -> bool { return x % 2 == 0; };
std::vector<int> data = {1, 3, 2, 5, 4};
// Алгоритм find_if берёт каждый элемент вектора и суёт его по одному в isEven.
auto it = std::find_if(data.begin(), data.end(), isEven);
if (it != data.end()) {
    std::cout << "Первое четное число: " << *it << std::endl; // Выведет: 2
}

Тут find_if — он как начальник, который каждому работнику (isEven) по одному сотруднику подсовывает и спрашивает: "Это он?" Работник смотрит на одного человека и говорит: "Да" или "Нет, не он".

Бинарный предикат (нужен для сортировки sort, поиска lower_bound):

// Функтор — бинарный предикат. Сравнивает две строки по длине. Два аргумента — уже повеселее.
struct CompareByLength {
    bool operator()(const std::string& a, const std::string& b) const {
        return a.size() < b.size();
    }
};
std::vector<std::string> words = {"apple", "pie", "banana", "a"};
// sort берёт парочку элементов и тыкает их в этот компаратор: "Ну что, 'apple' меньше 'pie'?"
std::sort(words.begin(), words.end(), CompareByLength());
// words теперь: ["a", "pie", "apple", "banana"] — отсортировали по росту, короче.

Вот тут уже sort работает как судья на конкурсе "Мисс Вселенная". Ему подают двух красоток (две строки), а предикат (компаратор) смотрит на обеих и решает, какая строка "меньше" (в нашем случае — короче). И так для всех пар, пока все не встанут по порядку.

Сейчас, в современном C++, народ вообще не парится и пихает лямбды прямо в вызов алгоритма, чтобы не заводить отдельные структуры. Получается коротко и ясно, хоть и выглядит иногда как заклинание древних:

std::sort(words.begin(), words.end(),
          [](const auto& a, const auto& b) { return a.size() < b.size(); });

Вот и вся магия. Главное — не перепутай, где сколько аргументов нужно. А то получится как в том анекдоте: придёт унарный предикат на место бинарного, ему два аргумента подадут, а он такой: "Бля, я с одним работаю, ёпта!" — и компилятор тебе впендюрит ошибку. А компилятор — он злой, как голодный кот, сука собака, терпения у него ноль ебать.