ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Categories:

C++: чем отличаются bind1st и bind2nd

Из задания к 9-му упражнению к 15-й главе (которая называется «Стандартная библиотека шаблонов (STL)») учебника Лафоре. Цитата, стр. 750-751:

В программе PLUSAIR показали, как применять функциональный объект plus<>() с алгоритмом accumulate(). В том примере не было необходимости в передаче каких-либо аргументов в функциональный объект, но иногда по логике работы программы это требуется. Оказывается, в данном случае нельзя просто указать аргумент в скобках, как мы привыкли делать. Вместо этого нужно использовать специальный «адаптер функции», называющийся bind1st или bind2nd, для связывания аргумента с функциональным объектом. Пусть, например, мы ищем строку SearchName в строковом контейнере names. В этом случае необходимо писать такое выражение:

ptr = find_if(names.begin(), names.end(), bind2nd(equal_to<string>(), SearchName));

Обычно в этом учебнике в каждой главе сначала дается теория, иллюстрируемая примерами в виде небольших программ, потом — два-три десятка вопросов по теории и 12 упражнений. Некоторые кусочки теории автор иногда выносит в задания к упражнениям. Здесь именно такой случай. То есть в теоретической части главы о функциональных адаптерах bind1st и bind2nd не рассказывалось.

Почему эти функции называются адаптерами? «Адаптер» — это по-русски «переходник», по крайней мере, в данном случае. Функциональный адаптер на входе получает функцию с двумя параметрами, а на выходе выдает функцию с одним параметром, вот и получается функциональный переходник. В приведенном выше примере с алгоритмом find_if в третьем параметре к нему должна быть задана функция с одним параметром (унарный предикат), а у нас имеется функция (функциональный объект) с двумя параметрами equal_to<>(), поэтому и используется переходник bind2nd.

Что означают названия bind1st и bind2nd? Слово «bind» в переводе с английского на русский означает «привязывать». Слово «1st» — это сокращение от «first» («первый»), а слово «2nd» — сокращение от «second» («второй»). То есть функциональный адаптер bind1st связывает заданное значение с первым параметром заданной функции, а bind2nd связывает заданное значение со вторым параметром заданной функции.

Источник:
https://en.cppreference.com/w/cpp/utility/functional/bind12

Самое интересное, что в указанном выше коде можно безболезненно заменить bind2nd на bind1st. Почему так происходит? Эти адаптеры взаимозаменяемы в случае симметричных операций, то есть таких бинарных операций, в которых перемена мест операндов не приводит к изменению результата. Симметричные операции — это сложение (при перемене мест слагаемых сумма не меняется), умножение и так далее. В нашем случае — проверка на равенство (equal_to<>()) — тоже симметричная операция.

Мне понравился пример, приведенный на сайте Stackoverflow.com, поясняющий разницу между bind1st и bind2nd:
int main () {
    auto p1 = bind1st(plus<int>(),10);
    auto p2 = bind2nd(plus<int>(),10);
    cout << p1(20) << endl;             // 10 + 20 = 30
    cout << p2(20) << endl;             // 20 + 10 = 30

    auto m1 = bind1st(minus<int>(),10);
    auto m2 = bind2nd(minus<int>(),10);
    cout << m1(20) << endl;             // 10 – 20 = –10
    cout << m2(20) << endl;             // 20 – 10 = 10
    return 0;
}

Тут, во-первых, хорошо видна работа функциональных адаптеров как переходников от функций с двумя параметрами к функциям с одним параметром. А во-вторых, видно, чем отличаются bind1st и bind2nd и как они выдают одинаковый результат в случае сложения параметров (симметричная операция) и разный результат в случае вычитания параметров (несимметричная операция).

Источник:
https://stackoverflow.com/questions/6863677/use-bind1st-or-bind2nd

В заключение надо бы отметить, что применение данных функциональных адаптеров не одобряется стандартом C++11, а стандартом C++17 эти функциональные адаптеры вообще удалены из языка C++. Как я понимаю, вместо них предлагается использовать функциональный адаптер std::bind:
https://en.cppreference.com/w/cpp/utility/functional/bind

Например, вместо
ptr = find_if(names.begin(), names.end(), bind2nd(equal_to<string>(), SearchName));
Можно написать так:
using namespace std::placeholders;
ptr = find_if(names.begin(), names.end(), bind(equal_to<string>(), _1, SearchName));

В моей среде разработки «Visual Studio Community 2017» и операционной системе «Windows 7» прекрасно работают оба эти варианта.

Кстати, решение 9-го упражнения к 15-й главе учебника Лафоре можно посмотреть тут.
Tags: Образование, Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments