C++
 Computer >> コンピューター >  >> プログラミング >> C++

C++のstd::bind関数とプレースホルダーで関数を束縛する方法

この記事では、C++におけるbind関数プレースホルダーについて解説します。プログラムを作っていると、既存の関数の動作を自分の目的に合わせて調整したい場面に遭遇することがあります。デフォルト引数などを活用すればその一部は実現できますが、より柔軟な制御が必要になることも少なくありません。

C++11では、こうしたニーズに応える新機能としてbind関数が導入されました。これにより、関数の引数を固定したり並べ替えたりする操作を、簡単な方法で行えるようになります。この機能を利用するには、<functional>ヘッダーをインクルードする必要があります。

bind関数はプレースホルダーと組み合わせることで、引数の位置や個数を柔軟に指定でき、望む出力に合わせて関数の振る舞いをカスタマイズできます。

プレースホルダーとは

プレースホルダーとは、関数に渡される値の「位置」を識別するための仕組みで、std::placeholders名前空間として提供されています。プレースホルダーは _1_2_3 のように表記され、_1 はバインド後の関数オブジェクトに渡される1番目の引数、_2 は2番目の引数に対応します。

また、bind関数は通常の関数だけでなく、メンバ関数へのポインタや関数オブジェクトも束縛できる点が特徴です。次のサンプルコードで、それぞれの使い方を確認してみましょう。

サンプルコード

#include <functional>
#include <iostream>

struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};

void print_num(int i) {
    std::cout << i << '\n';
}

struct PrintNum {
    void operator()(int i) const {
        std::cout << i << '\n';
    }
};

int main() {
    std::function<void(int)> f_display = print_num;
    f_display(-9);

    std::function<void()> f_display_42 = []() { print_num(42); };
    f_display_42();

    std::function<void()> f_display_31337 = std::bind(print_num, 31337);
    f_display_31337();

    std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
    const Foo foo(314159);
    f_add_display(foo, 1);

    std::function<int(Foo const&)> f_num = &Foo::num_;
    std::cout << "num_: " << f_num(foo) << '\n';

    using std::placeholders::_1;
    std::function<void(int)> f_add_display2 = std::bind(&Foo::print_add, foo, _1);
    f_add_display2(2);

    std::function<void(int)> f_add_display3 = std::bind(&Foo::print_add, &foo, _1);
    f_add_display3(3);

    std::function<void(int)> f_display_obj = PrintNum();
    f_display_obj(18);
}

実行結果

-9
42
31337
314160
num_: 314159
314161
314162
18

コードのポイント

  • f_display:通常の関数 print_numstd::function に格納して呼び出しています。
  • f_display_42:ラムダ式を使い、引数なしで print_num(42) を呼び出す関数オブジェクトを生成しています。
  • f_display_31337std::bind で引数を 31337 に固定しています。
  • f_add_displayf_num:メンバ関数やメンバ変数へのポインタも std::function に格納できます。
  • f_add_display2f_add_display3:プレースホルダー _1 を使うことで、メンバ関数の第2引数だけを受け取る新しい関数オブジェクトを作っています。オブジェクトをコピーで渡す場合と、アドレス(参照)で渡す場合の両方を示しています。
  • f_display_objoperator() を持つ関数オブジェクトも同様に扱えます。
  1. C++のフレンド関数とフレンドクラスの基本を解説

    C++におけるフレンド関数(friend function)とは、クラスのスコープ外で定義される関数でありながら、そのクラスのすべてのprivateメンバーおよびprotectedメンバーにアクセスする権限を持つ特別な関数です。フレンド関数のプロトタイプ(宣言)はクラス定義の中に記述しますが、あくまで「友達」として扱われるだけであり、クラスのメンバー関数にはなりません。この点は重要なポイントです。フレンドとして指定できるものフレンドとして宣言できるのは、以下のようなものです。通常の関数関数テンプレート他のクラスのメンバー関数クラス全体(この場合、そのクラスのすべてのメンバーがフレンドになります

  2. C++におけるendlと\nの違いとは?パフォーマンスを意識した使い分けのポイント

    C++の改行出力:endlと\nの基本的な違い C++で標準出力に改行を出力する方法として、主に「\\n」と「std::endl」の2つが使われます。一見どちらも同じように見えますが、内部動作には重要な違いがあります。 \\n の特徴 \\nは単純に改行文字を出力します。この際、改行コードはプラットフォーム固有の表現に変換されます。たとえばWindows環境では、自動的に「\\r\\n(キャリッジリターン+ラインフィード)」として扱われます。 std::endl の特徴 std::endlも同様に改行を出力しますが、それに加えてストリームのフラッシュ(バッファの強制書き出し)を行う点が異なりま