std::bind
Определено в заголовочном файле <functional> | ||
(1) | ||
template<class F, class... Args> /*не определено*/ bind( F&& f, Args&&... args); | (начиная с C++11) (до C++20) | |
template<class F, class... Args> constexpr/*не определено*/ bind( F&& f, Args&&... args); | (начиная с C++20) | |
(2) | ||
template<class R, class F, class... Args> /*не определено*/ bind( F&& f, Args&&... args); | (начиная с C++11) (до C++20) | |
template<class R, class F, class... Args> constexpr/*не определено*/ 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 | — | список аргументов для привязки, где несвязанные аргументы заменены заполнителями_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 иначе. Тип определяет следующие элементы:
Тип элемент | (до 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, неиспользуемые аргументы оцениваются и отбрасываются.
Вызов operator() это не генерирует исключениели является константным подвыражением(начиная с C++20) если и только если такова базовая операция INVOKE
. operator() участвует в разрешении перегрузки только в том случае, если операция INVOKE
имеет правильный формат при обработке как невычисленного операнда.
Если g является volatile-квалифицированным, программа некорректна.
Если INVOKE
(fd, w1, w2, ..., wN) никогда не может быть допустимым выражением для любых возможных значений w1,
w2, ...,
wN, поведение не определено.
[править]Привязанные аргументы
В операции INVOKE
или INVOKE<R>
Для каждого сохранённого аргумента arg_i определяется соответствующий связанный аргумент v_i следующим образом:
[править]Случай 1: обёртки ссылок
Если arg_i имеет тип std::reference_wrapper<T> (например, std::ref или std::cref использовались при первоначальном вызове std::bind
), тогда v_i равно arg_i.get() и его тип V_i
равен T&
: сохранённый аргумент передаётся по ссылке в вызываемый функциональный объект.
[править]Случай 2: выражения привязки
Если arg_i имеет тип T
, для которого std::is_bind_expression<T>::value имеет значение true (например, другое выражение std::bind
было передано непосредственно в начальный вызов std::bind
), тогда std::bind
выполняет композицию функций: вместо передачи функционального объекта, который будет возвращён подвыражением привязки, подвыражение с готовностью вызывается, и его возвращаемое значение передаётся внешнему вызываемому объекту. Если подвыражение привязки имеет какие-либо аргументы-заполнители, они используются совместно с внешней привязкой (выбранной из u1,
u2, ...
). В частности, v_i это arg_i(std::forward<Uj>(uj)...), а его тип V_i
есть std::result_of<Tcv &(Uj&&...)>::type&&(до C++17)std::invoke_result_t<Tcv &, 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, constint& 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(){usingnamespacestd::placeholders;// for _1, _2, _3... std::cout<<"1) переупорядочивание аргументов и передача по ссылке: ";int n =7;// (_1 и _2 взяты из std::placeholders, и представляют// будущие аргументы, которые будут переданы в f1)auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n); n =10; f1(1, 2, 1001);// 1 связан с _1, 2 связан с _2, 1001 не используется// вызывает f(2, 42, 1, n, 7) std::cout<<"2) достижение того же эффекта с помощью лямбда: "; n =7;auto lambda =[&ncref=n, n](auto a, auto b, auto/*не используется*/){ f(b, 42, a, ncref, n);}; n =10; lambda(1, 2, 1001);// то же, что и вызов f1(1, 2, 1001) std::cout<<"3) вложенные подвыражения связывания совместно используют заполнители: ";auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5); f2(10, 11, 12);// вызывает f(12, g(12), 12, 4, 5); std::cout<<"4) связывает RNG с распределением: ";std::default_random_engine e;std::uniform_int_distribution<> d(0, 10);auto rnd = std::bind(d, e);// копия e хранится в rndfor(int n=0; n<10;++n)std::cout<< rnd()<<' ';std::cout<<'\n'; std::cout<<"5) привязка к указателю на функцию-элемент: "; Foo foo;auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1); f3(5); std::cout<<"6) привязка к mem_fn, который является указателем на функцию-элемент: ";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) привязка к указателю на элемент данных: ";auto f5 = std::bind(&Foo::data, _1);std::cout<< f5(foo)<<'\n'; std::cout<<"8) привязка к mem_fn, который является указателем на элемент данных: ";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) использует умные указатели для вызова элементов по ссылке ""на объекты: ";std::cout<< f6(std::make_shared<Foo>(foo))<<' '<< f6(std::make_unique<Foo>(foo))<<'\n';}
Вывод:
1) переупорядочивание аргументов и передача по ссылке: 2 42 1 10 7 2) достижение того же эффекта с помощью лямбда: 2 42 1 10 7 3) вложенные подвыражения связывания совместно используют заполнители: 12 12 12 4 5 4) связывает RNG с распределением: 0 1 8 5 5 2 0 7 7 10 5) привязка к указателю на функцию-элемент: 100 6) привязка к mem_fn, который является указателем на функцию-элемент: 100 7) привязка к указателю на элемент данных: 10 8) привязка к mem_fn, который является указателем на элемент данных: 10 9) использует умные указатели для вызова элементов по ссылке на объекты: 10 10
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
LWG 2021 | C++11 | 1. привязанные аргументы не были переправлены в fd 2. в случае 2, тип V_i былstd::result_of<Tcv (Uj...)>::type | 1. переправлены 2. изменён на std::result_of<Tcv &(Uj&&...)>::type&& |
[править]Смотрите также
(C++20)(C++23) | связывает переменное количество аргументов по порядку с объектом функцией (шаблон функции) |
(C++11) | заполнители для несвязанных аргументов в выражении std::bind (константа) |
(C++11) | создаёт объект функцию из указателя на элемент (шаблон функции) |