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
в противном случае. Тип определяет следующие члены:
Тип-член
|
(до 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: reference wrappers
Если
arg_i
имеет тип
std::
reference_wrapper
<
T
>
(например, если в исходном вызове
std::bind
использовались
std::ref
или
std::cref
), тогда
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: placeholders
Если
arg_i
имеет тип
T
, для которого
std::
is_placeholder
<
T
>
::
value
не равно
0
(то есть, в качестве аргумента при первоначальном вызове
std::bind
был использован placeholder, такой как
std::placeholders::_1, _2, _3, ...
), тогда аргумент, указанный placeholder'ом (
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; // для _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) вложенные bind-выражения разделяют плейсхолдеры: "; 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) привязать генератор случайных чисел к распределению: "; std::default_random_engine e; std::uniform_int_distribution<> d(0, 10); auto rnd = std::bind(d, e); // копия e сохраняется в rnd for (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::данные, _1); std::cout << f5(foo) << '\n'; std::cout << "8) привязка к mem_fn, которая является указателем на член данных: "; auto ptr_to_data = std::mem_fn(&Foo::данные); 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) вложенные подвыражения bind разделяют плейсхолдеры: 12 12 12 4 5 4) привязка ГСЧ к распределению: 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++.
| DR | Применяется к | Поведение в опубликованной версии | Корректное поведение |
|---|---|---|---|
| LWG 2021 | C++11 |
1. ограниченные аргументы
не были перенаправлены в fd 2. в случае 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)
|
создает функциональный объект из указателя на член
(шаблон функции) |