名前空間
変種
操作

std::bind_front, std::bind_back

From cppreference.com
< cpp‎ | utility‎ | functional
 
 
ユーティリティライブラリ
言語サポート
型のサポート (基本型、RTTI)
ライブラリ機能検査マクロ (C++20)
プログラムユーティリティ
可変引数関数
コルーチンサポート (C++20)
契約サポート (C++26)
三方比較
(C++20)
(C++20)(C++20)(C++20)  
(C++20)(C++20)(C++20)

汎用ユーティリティ
関係演算子 (C++20で非推奨)
 
関数オブジェクト
部分関数適用
bind_frontbind_back
(C++20)(C++23)
(C++11)
関数の呼び出し
(C++17)(C++23)
恒等関数オブジェクト
(C++20)
透過的な演算子ラッパー
(C++14)
(C++14)
(C++14)
(C++14)  
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)
(C++14)

古いバインダとアダプタ
(C++17まで*)
(C++17まで*)
(C++17まで*)
(C++17まで*)  
(C++17まで*)
(C++17*まで)(C++17*まで)(C++17*まで)(C++17*まで)
(C++20まで*)
(C++20まで*)
(C++17*まで)(C++17*まで)
(C++17*まで)(C++17*まで)

(C++17まで*)
(C++17*まで)(C++17*まで)(C++17*まで)(C++17*まで)
(C++20まで*)
(C++20まで*)
 
ヘッダ <functional> で定義
std::bind_front
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以降)
std::bind_back
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_frontstd::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...).

以下の条件が true でなければならず、そうでなければプログラムはill-formedになります。

目次

[編集] パラメータ

f - 一部の引数にバインドされる 呼び出し可能 オブジェクト (関数オブジェクト、関数へのポインタ、関数への参照、メンバ関数へのポインタ、またはデータメンバへのポインタ)
args - 呼び出し可能ターゲットの (1,2) 最初の、または (3,4) 最後の sizeof...(Args) パラメータにバインドする引数のリスト
型要件
-
std::decay_t<F>MoveConstructible の要件を満たさなければなりません。
-
std::decay_t<Args>...MoveConstructible の要件を満たさなければなりません。
-
decltype(ConstFn)Callable の要件を満たさなければなりません。

[編集] 戻り値

2つの std::bind_front または std::bind_back 呼び出しが同じ引数で返されるオブジェクトの型が同じであること以外は、不定な型 T の関数オブジェクト(呼び出しラッパー)。

bind-partialstd::bind_front または std::bind_back とします。

返されるオブジェクトは以下のプロパティを持ちます。

bind-partial の戻り値の型

メンバーオブジェクト

返されるオブジェクトは、次のように保持されているかのように振る舞います。

1,3)std::decay_t<F> のメンバーオブジェクト fdstd::forward<F>(f) から直接非リスト初期化され、
1-4) std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...) で構築された std::tuple オブジェクト tup を保持します。ただし、返されるオブジェクトの代入動作は未規定であり、名前は説明目的のみです。

コンストラクタ

bind-partial の戻り値の型は、そのコピー/ムーブコンストラクタがメンバーごとのコピー/ムーブを実行するかのように振る舞います。すべてのメンバーオブジェクト(上記で指定されたもの)が CopyConstructible であれば CopyConstructible であり、そうでなければ MoveConstructible です。

メンバー関数 operator()

以前の (1,3) bind-partial(f, args...) または (2,4) bind-partial<ConstFn>(args...) の呼び出しから得られたオブジェクト G が与えられた場合、G を指定する glvalue g が関数呼び出し式 g(call_args...) で呼び出されると、次のように格納されたオブジェクトの呼び出しが行われます。

1) bind-partialstd::bind_front の場合、std::invoke(g.fd, std::get<Ns>(g.tup)..., call_args...),
2) bind-partialstd::bind_front の場合、std::invoke(ConstFn, std::get<Ns>(g.tup)..., call_args...),
3) bind-partialstd::bind_back の場合、std::invoke(g.fd, call_args..., std::get<Ns>(g.tup)...),
4) bind-partialstd::bind_back の場合、std::invoke(ConstFn, call_args..., std::get<Ns>(g.tup)...),

ここで、

  • Ns は整数パック 0, 1, ..., (sizeof...(Args) - 1) です。
  • g は、呼び出し式で lvalue であれば std::invoke 式では lvalue であり、そうでなければ rvalue です。したがって、std::move(g)(call_args...) はバインドされた引数を呼び出しにムーブできますが、g(call_args...) はコピーします。

g が揮発性修飾型を持つ場合、プログラムは ill-formed です。

メンバーの operator() は、それが呼び出す std::invoke 式が noexcept であれば noexcept です(つまり、基となる呼び出し演算子の例外指定を保持します)。

[編集] 例外

1,3) 格納された関数オブジェクトのコンストラクタを呼び出すことによってスローされた例外。
1-4) バインドされた引数のいずれかのコンストラクタを呼び出すことによってスローされた例外。

[編集] 注釈

これらの関数テンプレートは std::bind を置き換えることを目的としています。std::bind とは異なり、任意の引数再配置をサポートせず、ネストされたバインド式や std::reference_wrapper に特別な扱いはありません。一方、これらは呼び出しラッパーオブジェクトの右辺値カテゴリに注意を払い、基となる呼び出し演算子の例外指定を伝播します。

std::invoke で説明されているように、非静的メンバ関数へのポインタまたは非静的データメンバへのポインタを呼び出す場合、最初の引数は、メンバがアクセスされるオブジェクトへの参照またはポインタ(std::shared_ptrstd::unique_ptr などのスマートポインタを含む)でなければなりません。

std::bind_front または std::bind_back への引数はコピーまたはムーブされ、std::ref または std::cref でラップされない限り、参照によって渡されることはありません。

通常、(1) std::bind_front および (3) std::bind_back を使用して関数またはメンバ関数に引数をバインドする場合、言語がポインタを逆参照する必要なくどの関数を呼び出すべきかを正確に知っているにもかかわらず、関数ポインタと引数を一緒に格納する必要があります。これらのケースで「ゼロコスト」を保証するために、C++26 ではバージョン (2,4) (呼び出し可能オブジェクトを 非型テンプレートパラメータ の引数として受け入れる) が導入されました。

機能テストマクロ 規格 機能
__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)
  • TBD 関数テンプレート bind_front および bind_back [func.bind.partial]
  • C++23標準 (ISO/IEC 14882:2024)
  • 22.10.14 関数テンプレート bind_front および bind_back [func.bind.partial]
  • C++20 standard (ISO/IEC 14882:2020)
  • 20.14.14 関数テンプレート bind_front [func.bind.front]

[編集] 関連項目

(C++11)
1つ以上の引数を関数オブジェクトに束縛する
(関数テンプレート) [編集]
(C++11)
メンバへのポインタから関数オブジェクトを生成する
(関数テンプレート) [編集]
English 日本語 中文(简体) 中文(繁體)