yohhoyの日記

技術的メモをしていきたい日記

function/bindとクラスデータメンバ

C++11標準ライブラリのstd::functionstd::bindでは、クラスのデータメンバ(メンバ変数)を格納できる。

#include <functional>

struct A {
  int md;
  void mf(int v) { md = v * 2; }
} a;

// メンバ関数 A::mf (比較用)
std::function<void (A&, int)> f1 = &A::mf;
f1(a, 3);  // a.mf(3)
assert(a.md == 6);

using std::placeholers::_1;
std::function<void (int)> b1 = std::bind(&A::mf, std::ref(a), _1);
b1(4);     // a.mf(4)
assert(a.md == 8);

// データメンバ A::md
std::function<int& (A&)> f2 = &A::md;
f2(a) = 42;  // a.md = 42
assert(a.md == 42);

std::function<int& ()> b2 = std::bind(&A::md, std::ref(a));
b2() /= 2;   // a.md /= 2
assert(a.md == 21);

Boostライブラリのfunctionbindも同等機能を有する(Boost 1.49.0で動作確認)。ただしboost::bindの第一引数ではboost::function<int&(A&)>への明示的なキャストが必要。ここまでして使う意義があるかは疑問...

#include <boost/function.hpp>

a.md = 21;

boost::function<int& (A&)> f2 = &A::md;
f2(a) *= 2;
assert(a.md == 42);

// NG: boost::function<int& ()> b2 = boost::bind(&A::md, boost::ref(a))
boost::function<int& ()> b2 = boost::bind(
  boost::function<int& (A&)>(&A::md), boost::ref(a) );
b2() -= 10;
assert(a.md == 32);

2012-03-08追記:boost::bindの戻り値型R=int&を明示指定すれば良かった。(thanks to [twitter:@manga_osyo]さん)

boost::function<int& ()> b2 = boost::bind<int&>(&A::md, boost::ref(a));

N3337 20.8.2/p1, 20.8.11.2.4/p1-2より引用。(下線部は強調)

1 Define INVOKE(f, t1, t2, ..., tN) as follows:

  • (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;
  • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
  • t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;
  • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 is not one of the types described in the previous item;
  • f(t1, t2, ..., tN) in all other cases.

R operator()(ArgTypes... args) const
1 Effects: INVOKE(f, std::forward<ArgTypes>(args)..., R) (20.8.2), where f is the target object (20.8.1) of *this.
2 Returns: Nothing if R is void, otherwise the return value of INVOKE(f, std::forward<ArgTypes>(args)..., R).

関連URL