std::bind
Определено в заголовке <functional> | ||
|---|---|---|
| (1) | ||
template< class F, class... Args > /* unspecified */ bind( F&& f, Args&&... args ); |
(с C++11) (до C++20) | |
template< class F, class... Args > constexpr /* unspecified */ bind( F&& f, Args&&... args ); | (с C++20) | |
| (2) | ||
template< class R, class F, class... Args > /* unspecified */ bind( F&& f, Args&&... args ); |
(с C++11) (до C++20) | |
template< class R, class F, class... Args > constexpr /* unspecified */ bind( F&& f, Args&&... args ); | (с C++20) |
Шаблон функции std::bind генерирует обёртку для вызова перенаправления для f. Вызов этой обёртки эквивалентен вызову f с некоторыми его аргументами, связанными с args.
Если std::is_constructible<std::decay<F>::type, F>::value является false, или std::is_constructible<std::decay<Arg_i>::type, Arg_i>::value является false для любого типа Arg_i в Args, программа некорректна.
Если std::decay<Ti>::type или любой тип в Args не является MoveConstructible или Destructible, поведение не определено.
Параметры
| f | - | Объект Callable (объект-функция, указатель на функцию, ссылка на функцию, указатель на член-функцию или указатель на член данных), который будет связан с некоторыми аргументами |
| args | - | Список аргументов для привязки, при этом несвязанные аргументы заменяются плейсхолдерами placeholders _1, _2, _3... из пространства имён std::placeholders |
Значение результата
Объект-функция g неопределённого типа T, для которой std::is_bind_expression<T>::value является true. У неё есть следующие члены:
std::bind тип результата
Члены-объекты
Тип результата std::bind содержит член-объект типа std::decay<F>::type , сконструированный из std::forward<F>(f), и один объект для каждого из args..., типа std::decay<Arg_i>::type, аналогично сконструированный из std::forward<Arg_i>(arg_i).
Конструкторы
Тип результата std::bind является CopyConstructible, если все его член-объекты (указанные выше) являются CopyConstructible, и является MoveConstructible в противном случае. Тип определяет следующие члены:
Тип члена 1) (устарело в C++17) Если | (до C++20) |
Член-функция operator()
При вызове g в выражении вызова функции g(u1, u2, ... uM), происходит вызов сохранённого объекта, как если бы это было
INVOKE(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN)), илиINVOKE<R>(fd, std::forward<V1>(v1), std::forward<V2>(v2), ..., std::forward<VN>(vN)),где fd имеет тип std::decay<F>::type, значения и типы связанных аргументов v1, v2, ..., vN определяются, как указано ниже.
Если некоторые аргументы, предоставленные при вызове g(), не совпадают ни с одним из плейсхолдеров, сохранённых в g, то неиспользуемые аргументы вычисляются и отбрасываются.
Вызов оператора() является небросковым или является постоянным подвыражением(с C++20) тогда и только тогда, когда таковым является основное INVOKE действие. Оператор() участвует в разрешении перегрузки только тогда, когда INVOKE действие корректно, когда рассматривается как неоцениваемый операнд.
Если g имеет квалификатор volatile, программа некорректна.
Если INVOKE(fd, w1, w2, ..., wN) никогда не может быть допустимым выражением для каких-либо возможных значений w1, w2, ..., wN, поведение не определено.
Связанные аргументы
Для каждого сохранённого аргумента arg_i, соответствующий связанный аргумент v_i в операции INVOKE или INVOKE<R> определяется следующим образом:
Случай 1: обёртки ссылок
Если arg_i имеет тип std::reference_wrapper<T> (например, std::ref или std::cref использовались в начальном вызове std::bind), то v_i является arg_i.get(), и его тип V_i является T&: сохранённый аргумент передаётся по ссылке в вызываемый объект-функцию.
Случай 2: выражения bind
Если arg_i имеет тип T, для которого std::is_bind_expression<T>::value является true (например, другое выражение std::bind было передано напрямую в начальный вызов std::bind), то std::bind выполняет композицию функций: вместо передачи объекта-функции, которую вернёт выражение bind, выражение вызывается немедленно, а его возвращаемое значение передаётся во внешний вызываемый объект. Если у выражения bind есть какие-либо аргументы-заменители, они разделяются с внешним bind (выбираются из u1, u2, ...). В частности, v_i является arg_i(std::forward<Uj>(uj)...), а его тип V_i является std::result_of<T cv &(Uj&&...)>::type&&(до C++17)std::invoke_result_t<T cv &, Uj&&...>&&(с C++17) (квалификация cv совпадает с квалификацией g).
Случай 3: плейсхолдеры
Если arg_i имеет тип T, для которого std::is_placeholder<T>::value не является 0 (т.е. был использован плейсхолдер, например, std::placeholders::_1, _2, _3, ..., в качестве аргумента в начальный вызов std::bind), то аргумент, указанный плейсхолдером (u1 для _1, u2 для _2 и т. д.) передаётся вызываемому объекту: v_i является std::forward<Uj>(uj), а его тип V_i является Uj&&.
Случай 4: обычные аргументы
В противном случае, arg_i передаётся в вызываемый объект как аргумент lvalue: v_i просто arg_i, а его тип V_i является T cv &, где cv — та же квалификация cv, что и у g.
Исключение
Бросает исключение только если при создании std::decay<F>::type из std::forward<F>(f) происходит бросок, или если при создании std::decay<Arg_i>::type из соответствующего std::forward<Arg_i>(arg_i) происходит бросок, где Arg_i — тип i-го аргумента, а arg_i — i-й аргумент в Args... args.
Примечания
Как описано в Callable, при вызове указателя на нестатическую функцию-член или указателя на нестатический член данных, первый аргумент должен быть ссылкой или указателем (включая, возможно, умный указатель, такой как std::shared_ptr и std::unique_ptr) на объект, члены которого будут обработаны.
Аргументы для привязки копируются или перемещаются, и никогда не передаются по ссылке, если не заключены в std::ref или std::cref.
Позволяется дублировать заполнительные места в одном выражении привязки (например, несколько _1), но результаты определены корректно только если соответствующий аргумент (u1) является lvalue или неперемещаемым rvalue.
Пример
#include <functional>
#include <iostream>
#include <memory>
#include <random>
void f(int n1, int n2, int n3, const int& n4, int n5)
{
std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}
int g(int n1)
{
return n1;
}
struct Foo
{
void print_sum(int n1, int n2)
{
std::cout << n1 + n2 << '\n';
}
int data = 10;
};
int main()
{
using namespace std::placeholders; // for _1, _2, _3...
std::cout << "1) argument reordering and pass-by-reference: ";
int n = 7;
// (_1 and _2 are from std::placeholders, and represent future
// arguments that will be passed to f1)
auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
n = 10;
f1(1, 2, 1001); // 1 is bound by _1, 2 is bound by _2, 1001 is unused
// makes a call to f(2, 42, 1, n, 7)
std::cout << "2) achieving the same effect using a lambda: ";
n = 7;
auto lambda = [&ncref = n, n](auto a, auto b, auto /*unused*/)
{
f(b, 42, a, ncref, n);
};
n = 10;
lambda(1, 2, 1001); // same as a call to f1(1, 2, 1001)
std::cout << "3) nested bind subexpressions share the placeholders: ";
auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
f2(10, 11, 12); // makes a call to f(12, g(12), 12, 4, 5);
std::cout << "4) bind a RNG with a distribution: ";
std::default_random_engine e;
std::uniform_int_distribution<> d(0, 10);
auto rnd = std::bind(d, e); // a copy of e is stored in rnd
for (int n = 0; n < 10; ++n)
std::cout << rnd() << ' ';
std::cout << '\n';
std::cout << "5) bind to a pointer to member function: ";
Foo foo;
auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
f3(5);
std::cout << "6) bind to a mem_fn that is a pointer to member function: ";
auto ptr_to_print_sum = std::mem_fn(&Foo::print_sum);
auto f4 = std::bind(ptr_to_print_sum, &foo, 95, _1);
f4(5);
std::cout << "7) bind to a pointer to data member: ";
auto f5 = std::bind(&Foo::data, _1);
std::cout << f5(foo) << '\n';
std::cout << "8) bind to a mem_fn that is a pointer to data member: ";
auto ptr_to_data = std::mem_fn(&Foo::data);
auto f6 = std::bind(ptr_to_data, _1);
std::cout << f6(foo) << '\n';
std::cout << "9) use smart pointers to call members of the referenced objects: ";
std::cout << f6(std::make_shared<Foo>(foo)) << ' '
<< f6(std::make_unique<Foo>(foo)) << '\n';
}Вывод:
1) argument reordering and pass-by-reference: 2 42 1 10 7 2) achieving the same effect using a lambda: 2 42 1 10 7 3) nested bind subexpressions share the placeholders: 12 12 12 4 5 4) bind a RNG with a distribution: 0 1 8 5 5 2 0 7 7 10 5) bind to a pointer to member function: 100 6) bind to a mem_fn that is a pointer to member function: 100 7) bind to a pointer to data member: 10 8) bind to a mem_fn that is a pointer to data member: 10 9) use smart pointers to call members of the referenced objects: 10 10
Отчёты о дефектах
Следующие отчёты о дефектах, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применимо к | Поведение при публикации | Правильное поведение |
|---|---|---|---|
| LWG 2021 | C++11 | 1. ограниченные аргументы не были переданы в fd2. в случае 2, тип V_i былstd::result_of<T cv (Uj...)>::type | 1. передан 2. изменён на std::result_of<T cv &(Uj&&...)>::type&& |
См. также
|
(C++20)(C++23) | привязывает переменное количество аргументов в порядке к объекту функции (шаблон функции) |
|
(C++11) | плейсхолдеры для свободных аргументов в выражении std::bind (константа) |
|
(C++11) | создаёт объект функции из указателя на член (шаблон функции) |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/utility/functional/bind