std::bind
Определено в заголовочном файле <functional> | ||
|---|---|---|
template< class F, class... Args > /* unspecified */ bind( F&& f, Args&&... args ); | (1) |
(начиная с C++11) (constexpr начиная с C++20) |
template< class R, class F, class... Args > /* unspecified */ bind( F&& f, Args&&... args ); | (2) |
(начиная с C++11) (constexpr начиная с 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 в противном случае. Тип определяет следующие члены:
Тип-член 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, неиспользуемые аргументы вычисляются и отбрасываются.
Вызов operator() является невыбрасывающим исключения или является константным подвыражением(начиная с C++20) тогда и только тогда, когда таким является базовая операция INVOKE. operator() участвует в разрешении перегрузки только в том случае, если операция 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-квалификация совпадает с cv-квалификацией g).
Случай 3: заполнители
Если arg_i имеет тип T, для которого std::is_placeholder<T>::value не имеет значения 0 (то есть в качестве аргумента при первоначальном вызове std::bind использовался заполнитель, например std::placeholders::_1, _2, _3, ...), вызываемому объекту передается аргумент, указанный заполнителем (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) на объект, член которого будет доступен.
Аргументы для bind копируются или перемещаются и никогда не передаются по ссылке, если только они не обернуты в std::ref или std::cref.
Повторяющиеся заполнители в одном выражении bind (например, несколько _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/cpp/utility/functional/bind