Spec-Zone.ru › C++

std::bind_front, std::bind_back

Определено в заголовке <functional>
template< class F, class... Args >
constexpr /* unspecified */ bind_front( F&& f, Args&&... args );
(1) (с C++20)
template< auto ConstFn, class... Args >
constexpr /* unspecified */ bind_front( Args&&... args );
(2) (с C++26)
template< class F, class... Args >
constexpr /* unspecified */ bind_back( F&& f, Args&&... args );
(3) (с C++23)
template< auto ConstFn, class... Args >
constexpr /* unspecified */ bind_back( Args&&... args );
(4) (с C++26)

Шаблонные функции std::bind_front и std::bind_back генерируют обёртку вызова с идеальной передачей, которая позволяет вызвать целевой вызываемый объект с его (1,2) первыми или (3,4) последними sizeof...(Args) параметрами, привязанными к args.

1,3) Обёртка вызова содержит копию целевого вызываемого объекта f.
2,4) Обёртка вызова не содержит целевой вызываемый объект (он определяется статически).
1) std::bind_front(f, bound_args...)(call_args...) эквивалентно
std::invoke(f, bound_args..., call_args...).
2) std::bind_front<ConstFn>(bound_args...)(call_args...) эквивалентно
std::invoke(ConstFn, bound_args..., call_args...).
3) std::bind_back(f, bound_args...)(call_args...) эквивалентно
std::invoke(f, call_args..., bound_args...).
4) std::bind_back<ConstFn>(bound_args...)(call_args...) эквивалентно
std::invoke(ConstFn, call_args..., bound_args...).

Следующие условия должны быть истинными, в противном случае программа является некорректной:

  • (1,3) std::is_constructible_v<std::decay_t<F>, F>,
  • (1,3) std::is_move_constructible_v<std::decay_t<F>>,
  • (2,4) Если decltype(ConstFn) является указателем или указателем на член, то ConstFn не является нулевым указателем,
  • (std::is_constructible_v<std::decay_t<Args>, Args> && ...),
  • (std::is_move_constructible_v<std::decay_t<Args>> && ...).

Параметры

f - Объект Callable (объект-функция, указатель на функцию, ссылка на функцию, указатель на функцию-член, или указатель на член данных), который будет привязан к некоторым аргументам
args - Список аргументов, которые нужно привязать к (1,2) первым или (3,4) последним sizeof...(Args) параметрам целевого вызываемого объекта
Требования к типу
-std::decay_t<F> должно соответствовать требованиям MoveConstructible.
-std::decay_t<Args>... должно соответствовать требованиям MoveConstructible.
-decltype(ConstFn) должно соответствовать требованиям Callable.

Значение возврата

Объект-функция (обёртка вызова) типа T, который не определён, за исключением того, что типы объектов, возвращаемых двумя вызовами std::bind_front или std::bind_back с одинаковыми аргументами, одинаковы.

Пусть bind-partial — это либо std::bind_front либо std::bind_back.

Возвращаемый объект имеет следующие свойства:

Тип возвращаемого значения bind-части

Члены-объекты

Возвращаемый объект ведет себя так, как если бы он содержал:

1,3) Член-объект fd типа std::decay_t<F> инициализированный напрямую без списка из std::forward<F>(f), и
1-4) Объект std::tuple tup построенный с помощью std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...), за исключением того, что поведение присваивания возвращаемого объекта не определено, а имена указаны только для пояснения.

Конструкторы

Тип возвращаемого значения bind-partial ведет себя так, как если бы его конструкторы копирования/перемещения выполняли членное копирование/перемещение. Он является CopyConstructible, если все его члены-объекты (указанные выше) являются CopyConstructible, и MoveConstructible в противном случае.

Член-функция operator()

Учитывая объект G полученный ранее из (1,3) bind-partial(f, args...) или (2,4) bind-partial<ConstFn>(args...), когда левореферентный g обозначающий G вызывается в выражении вызова функции g(call_args...), происходит вызов сохранённого объекта, как будто:

1) std::invoke(g.fd, std::get<Ns>(g.tup)..., call_args...), когда bind-partial является std::bind_front,
2) std::invoke(ConstFn, std::get<Ns>(g.tup)..., call_args...), когда bind-partial является std::bind_front,
3) std::invoke(g.fd, call_args..., std::get<Ns>(g.tup)...), когда bind-partial является std::bind_back,
4) std::invoke(ConstFn, call_args..., std::get<Ns>(g.tup)...), когда bind-partial является std::bind_back,

где

  • Ns — это целое число pack 0, 1, ..., (sizeof...(Args) - 1),
  • g — это левореферентное значение в выражении std::invoke, если это левореферентное значение в выражении вызова, и это правореферентное значение в противном случае. Таким образом, std::move(g)(call_args...) может переместить привязанные аргументы в вызов, где g(call_args...) бы скопировал.

Программа является некорректной, если g имеет тип с квалификатором volatile.

Член operator() является noexcept, если выражение std::invoke вызова является noexcept (иными словами, оно сохраняет спецификацию исключений основного оператора вызова).

Исключения

1,3) Бросить любое исключение, брошенное при вызове конструктора сохранённого объекта функции.
1-4) Бросить любое исключение, брошенное при вызове конструктора любого из привязанных аргументов.

Примечания

Эти шаблонные функции предназначены для замены std::bind. В отличие от std::bind, они не поддерживают произвольную перестановку аргументов и не имеют специального обращения к вложенным выражениям bind или std::reference_wrapper. С другой стороны, они обращают внимание на категорию значений объекта обёртки вызова и распространяют спецификацию исключений основного оператора вызова.

Как описано в std::invoke, при вызове указателя на функцию-член или указателя на член данных, не являющихся статическими, первый аргумент должен быть ссылкой или указателем (включая, возможно, умные указатели, такие как std::shared_ptr и std::unique_ptr ) на объект, члены которого будут обработаны.

Аргументы std::bind_front или std::bind_back копируются или перемещаются, и никогда не передаются по ссылке, если не заключены в std::ref или std::cref.

Обычно привязка аргументов к функции или функции-члену с использованием (1) std::bind_front и (3) std::bind_back требует хранения указателя на функцию вместе с аргументами, даже если язык точно знает, какую функцию вызывать, без необходимости разыменования указателя. Чтобы гарантировать «нулевую стоимость» в этих случаях, C++26 вводит версии (2,4) (которые принимают вызываемый объект как аргумент для параметра шаблона нетипа).

Макрос проверки возможностей Значение Std Функция
__cpp_lib_bind_front 201907L (C++20) std::bind_front, (1)
202306L (C++26) Разрешить передачу вызываемых объектов в качестве параметров шаблона нетипа для std::bind_front, (2)
__cpp_lib_bind_back 202202L (C++23) std::bind_back, (3)
202306L (C++26) Разрешить передачу вызываемых объектов в качестве параметров шаблона нетипа для std::bind_back, (4)

Возможная реализация

(2) bind_front
namespace detail
{
    template<class T, class U>
    struct copy_const
        : std::conditional<std::is_const_v<T>, U const, U> {};
 
    template<class T, class U,
             class X = typename copy_const<std::remove_reference_t<T>, U>::type>
    struct copy_value_category
        : std::conditional<std::is_lvalue_reference_v<T&&>, X&, X&&> {};
 
    template <class T, class U>
    struct type_forward_like
        : copy_value_category<T, std::remove_reference_t<U>> {};
 
    template <class T, class U>
    using type_forward_like_t = typename type_forward_like<T, U>::type;
}
 
template<auto ConstFn, class... Args>
constexpr auto bind_front(Args&&... args)
{
    using F = decltype(ConstFn);
 
    if constexpr (std::is_pointer_v<F> or std::is_member_pointer_v<F>)
        static_assert(ConstFn != nullptr);
 
    return
        [... bound_args(std::forward<Args>(args))]<class Self, class... T>
        (
            this Self&&, T&&... call_args
        )
        noexcept
        (
            std::is_nothrow_invocable_v<F,
                detail::type_forward_like_t<Self, std::decay_t<Args>>..., T...>
        )
        -> std::invoke_result_t<F,
                detail::type_forward_like_t<Self, std::decay_t<Args>>..., T...>
        {
            return std::invoke(ConstFn, std::forward_like<Self>(bound_args)...,
                               std::forward<T>(call_args)...);
        };
}
(4) bind_back
namespace detail { /* is the same as above */ }
 
template<auto ConstFn, class... Args>
constexpr auto bind_back(Args&&... args)
{
    using F = decltype(ConstFn);
 
    if constexpr (std::is_pointer_v<F> or std::is_member_pointer_v<F>)
        static_assert(ConstFn != nullptr);
 
    return
        [... bound_args(std::forward<Args>(args))]<class Self, class... T>
        (
            this Self&&, T&&... call_args
        )
        noexcept
        (
            std::is_nothrow_invocable_v<F,
                detail::type_forward_like_t<Self, T..., std::decay_t<Args>>...>
        )
        -> std::invoke_result_t<F,
                detail::type_forward_like_t<Self, T..., std::decay_t<Args>>...>
        {
            return std::invoke(ConstFn, std::forward<T>(call_args)...,
                               std::forward_like<Self>(bound_args)...);
        };
}

Пример

#include <cassert>
#include <functional>
 
int minus(int a, int b)
{
    return a - b;
}
 
struct S
{
    int val;
    int minus(int arg) const noexcept { return val - arg; }
};
 
int main()
{
    auto fifty_minus = std::bind_front(minus, 50);
    assert(fifty_minus(3) == 47); // equivalent to: minus(50, 3) == 47
 
    auto member_minus = std::bind_front(&S::minus, S{50});
    assert(member_minus(3) == 47); //: S tmp{50}; tmp.minus(3) == 47
 
    // Noexcept-specification is preserved:
    static_assert(!noexcept(fifty_minus(3)));
    static_assert(noexcept(member_minus(3)));
 
    // Binding of a lambda:
    auto plus = [](int a, int b) { return a + b; };
    auto forty_plus = std::bind_front(plus, 40);
    assert(forty_plus(7) == 47); // equivalent to: plus(40, 7) == 47
 
#if __cpp_lib_bind_front >= 202306L
    auto fifty_minus_cpp26 = std::bind_front<minus>(50);
    assert(fifty_minus_cpp26(3) == 47);
 
    auto member_minus_cpp26 = std::bind_front<&S::minus>(S{50});
    assert(member_minus_cpp26(3) == 47);
 
    auto forty_plus_cpp26 = std::bind_front<plus>(40);
    assert(forty_plus(7) == 47);
#endif
 
#if __cpp_lib_bind_back >= 202202L
    auto madd = [](int a, int b, int c) { return a * b + c; };
    auto mul_plus_seven = std::bind_back(madd, 7);
    assert(mul_plus_seven(4, 10) == 47); //: madd(4, 10, 7) == 47
#endif
 
#if __cpp_lib_bind_back >= 202306L
    auto mul_plus_seven_cpp26 = std::bind_back<madd>(7);
    assert(mul_plus_seven_cpp26(4, 10) == 47);
#endif
}

Ссылки

  • Стандарт C++26 (ISO/IEC 14882:2026):
    • Функции-шаблоны bind_front и bind_back [func.bind.partial] (TBD)
  • Стандарт C++23 (ISO/IEC 14882:2023):
    • 22.10.14 Функции-шаблоны bind_front и bind_back [func.bind.partial]
  • Стандарт C++20 (ISO/IEC 14882:2020):
    • Шаблон функции bind_front [func.bind.front]

См. также

bind
(C++11)
связывает один или несколько аргументов с объектом функции
(шаблон функции)
mem_fn
(C++11)
создает объект функции из указателя на член
(шаблон функции)

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/utility/functional/bind_front

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API