C++のレガシー関数アダプタ:std::mem_funによるメンバ関数ポインタのラッピング

C++において、関数呼び出し演算子operator()を定義したオブジェクトは関数オブジェクトと呼ばれます。標準ライブラリは、これらのオブジェクトを生成・操作する機能を提供しており、その一つにメンバ関数ポインタから関数オブジェクトを生成するアダプタが存在します。

レガシーなバインダとアダプタ

メンバ関数を一般的な呼び出し可能オブジェクトとして扱うために、C++98の標準ライブラリではstd::mem_funおよび関連するヘルパークラスが導入されました。これらの機能は、より汎用的で強力なstd::mem_fnstd::bindの登場により、C++11で非推奨(deprecated)とされ、最終的にC++17で削除されました。

std::mem_funによるラッパーの生成

std::mem_funは、クラスのメンバ関数へのポインタを受け取り、そのクラスのオブジェクトへのポインタを引数として受け取る関数オブジェクトを生成します。これにより、標準アルゴリズム(std::for_eachstd::transformなど)を用いて、ポインタのコンテナに格納されたオブジェクトのメンバ関数を簡潔に呼び出すことが可能になります。

この関数テンプレートは、メンバ関数がconstであるかどうか、および引数の数に基づいて、以下のような内部的なラッパークラスを自動的に選択してインスタンス化します。

  • std::mem_fun_t: 引数なし、非constメンバ関数用
  • std::const_mem_fun_t: 引数なし、constメンバ関数用
  • std::mem_fun1_t: 引数1つ、非constメンバ関数用
  • std::const_mem_fun1_t: 引数1つ、constメンバ関数用

std::mem_funとよく似た機能にstd::mem_fun_refがありますが、前者はオブジェクトへの「ポインタ」を引数にとるのに対し、後者はオブジェクトへの「参照」を引数に取る点が異なります。

実装例

以下のサンプルコードは、std::mem_funを使用して、オブジェクトへのポインタを格納したコンテナに対し、メンバ関数を呼び出す様子を示しています。ここではWidgetクラスを定義し、そのメソッドgetStatusを呼び出して結果を別のコンテナにコピーしています。

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <string>
#include <iterator>

// サンプル用クラス定義
class Widget {
public:
    Widget(int id, const std::string& status) 
        : identifier(id), state(status) {}

    // 状態を取得するconstメンバ関数
    std::string getStatus() const {
        return state;
    }

    // IDを取得するconstメンバ関数
    int getIdentifier() const {
        return identifier;
    }

private:
    int identifier;
    std::string state;
};

int main() {
    // Widgetへのポインタのコンテナを作成
    std::vector<Widget*> gadgetList;
    gadgetList.push_back(new Widget(101, "Active"));
    gadgetList.push_back(new Widget(102, "Idle"));
    gadgetList.push_back(new Widget(103, "Disabled"));

    // 結果を格納する文字列のコンテナ
    std::vector<std::string> statusLog;
    statusLog.resize(gadgetList.size());

    // std::mem_funを使用して、ポインタのコンテナからメンバ関数を呼び出し、結果をtransformする
    std::transform(gadgetList.begin(), gadgetList.end(), 
                   statusLog.begin(), 
                   std::mem_fun(&Widget::getStatus));

    // 結果の出力
    std::cout << "Gadget Statuses:" << std::endl;
    std::copy(statusLog.begin(), statusLog.end(), 
              std::ostream_iterator<std::string>(std::cout, "\n"));

    // IDの取得例
    std::vector<int> idLog;
    idLog.resize(gadgetList.size());
    
    std::transform(gadgetList.begin(), gadgetList.end(), 
                   idLog.begin(), 
                   std::mem_fun(&Widget::getIdentifier));

    std::cout << "\nGadget IDs:" << std::endl;
    std::copy(idLog.begin(), idLog.end(), 
              std::ostream_iterator<int>(std::cout, " "));
    std::cout << std::endl;

    // メモリの解放
    for (auto ptr : gadgetList) {
        delete ptr;
    }

    return 0;
}

出力結果

Gadget Statuses:
Active
Idle
Disabled

Gadget IDs:
101 102 103

タグ: cpp STL functional mem_fun deprecated

6月14日 22:16 投稿