Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

BoostでC++0xのライブラリ「TR1」を先取りしよう (4)

bindとfunction

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/02/12 14:00

C++の新しい規格「C++0x」に追加予定のライブラリ群「TR1」の概要を、Boost版を使って解説します(Visual Studio 2008にも追加パッケージとして供給される予定)。第4回は「bind」と「function」についてです。

目次

はじめに

 C++の新しい規格「C++0x」では、言語とライブラリの両面から便利な機能が追加されます。「TR1」(Technical Report 1)はC++0xのライブラリ部で、標準C++に新たに追加されるライブラリの多くはBoostの中から選ばれたものです。2008年春にリリースが予定されているVisual Studio 2008にも追加パッケージとして供給されるとの情報を得ています。

 TR1に収録されたクラス/関数の中からいくつかをピックアップし、その概要と使い方を予習しておきましょう。第4回はbindとfunctionです。

これまでの記事

 BoostのインストールについてはBoostでC++0xのライブラリ「TR1」を先取りしよう (1)をご覧ください。
 2008年1月現在、Visual C++ 2008 Feature Pack Betaおよびそのドキュメントが公開されています。Feature PackはMFCの追加機能とTR1をサポートするライブラリのパッチです。現Betaの段階ではVisual Studio 2008の英語版にのみ対応しています。

bind

 標準C++ライブラリが提供するさまざまな関数には1つ(あるいは2つ)の引数を与える関数オブジェクトを引き渡すものが多数あります。

正数はいくつある?
#include <iostream>
#include <algorithm>

using namespace std;

// n が正数なら true を返す
bool is_positive(int n) {
  return n > 0;
}

int main() {
  const int N = 8;
  int data[N] = { -3, -2, -1,  0,  1,  2,  3,  4 };
 /* count_if(first, last, pred):
   * first以上last未満の範囲にあるxに対し、
   * pred(*x) が真(!=0)となるものの個数を返す */
  cout << count_if(data, data+N, &is_positive) << " positives\n";
}
実行結果
4 positives

 では同じく標準C++ライブラリの関数オブジェクトless<T>を使って上記のis_positiveに置き換えられないか考えてみましょう。

 関数オブジェクトless<T>のメンバ関数operator()は引数を2つ取り、第1引数が第2引数より小さければtrueを返します。

関数オブジェクト less
std::less<int> int_less;
bool result = int_less(1,2); // true
     result = int_less(2,1); // false
     result = int_less(2,2); // false

 このless<int>を先ほどのアルゴリズム:count_ifの第3引数に使うため、binder1stless<int>::operator()の第1引数を0で束縛(固定)します。

bind1st/bind2ndによる束縛
#include <iostream>
#include <algorithm>
#include <functional>

using namespace std;

int main() {
  const int N = 8;
  int data[N] = { -3, -2, -1,  0,  1,  2,  3,  4 };
  // 第1引数を0で束縛することで、
  // 引数が整数ならtrueを返す関数オブジェクトとなる。
  cout << count_if(data, data+N, bind1st(less<int>(),0)) 
       << " positives\n";
  // 第2引数を0で束縛すれば、正数ならtrueを返す。
  cout << count_if(data, data+N, bind2nd(less<int>(),0)) 
       << " negatives\n";
}
実行結果
4 positives
3 negatives

 それでは「-3以上、3未満の要素数」を求めるにはどうしましょうか。

midがlo以上、hi未満ならtrueを返す
template<typename T>
bool in_between(T lo, T mid, T hi) {
  return lo <= mid && mid < hi;
}

 こんな関数in_betweenを用意し、第1引数を-3、第3引数を3で束縛すればよいのですが、標準C++が用意してくれているバインダ(束縛関数)は二項関数オブジェクトに対するbind1stbind2ndだけです。

 TR1はこのように制限の厳しいバインダを一般化し、operator()に与える任意の引数を束縛する汎用バインダ:bindを提供してくれます。

bindを使って-3以上、3未満の要素を数える
#include <iostream>
#include <algorithm>
#include <functional>
#include <boost/tr1/functional.hpp> 

using namespace std;

// lo <= mid < hi なら true を返す
template<typename T>
bool in_between(T lo, T mid, T hi) {
  return lo <= mid && mid < hi;
}

int main() {
  const int N = 8;
  int data[N] = { -3, -2, -1,  0,  1,  2,  3,  4 };
  // プレースホルダ: _1, _2, ... を有効にする
  using namespace tr1::placeholders;
  // in_betweenの第1,第3引数をそれぞれ-3,3で束縛する
  cout << count_if(data, data+N, 
                   tr1::bind(&in_between<int>, -3, _1, 3)) 
                     << " numbers are [-3,3)\n";
}
実行結果
6 numbers are [-3,3)

 プレースホルダの数/位置/順序は自由に選択できますから、以下のサンプルのように三項関数の引数1つを束縛して二項関数とすることもできます。

要素のソート
#include <iostream>
#include <algorithm>
#include <functional>
#include <boost/tr1/functional.hpp> 

using namespace std;

// 昇順/降順のどちらにも使える比較関数
template<typename T>
bool compare(T x, T y, bool descend) {
  return descend ? (x < y) : (y < x);
}

int main() {
  const int N = 8;
  int data[N] = { 0, -1, 1, -2, 2, -3, 3, 4 };
  using namespace tr1::placeholders;
  // 降順にソート ※ sortの第3引数は二項関数オブジェクト
  sort(data, data+N, tr1::bind(&compare<int>, _1, _2, false));
  for ( int i = 0; i < N; ++i ) cout << data[i] << ' '; cout << endl;
  // 昇順にソート
  sort(data, data+N, tr1::bind(&compare<int>, _1, _2, true));
  for ( int i = 0; i < N; ++i ) cout << data[i] << ' '; cout << endl;
  // これもやっぱり昇順にソート(プレースホルダの順序に注目)
  sort(data, data+N, tr1::bind(&compare<int>, _2, _1, false));
  for ( int i = 0; i < N; ++i ) cout << data[i] << ' '; cout << endl;
}
実行結果
4 3 2 1 0 -1 -2 -3
-3 -2 -1 0 1 2 3 4
-3 -2 -1 0 1 2 3 4

 さらにbindはクラスのメンバ関数さえもバインドしてくれます。

メンバ関数のバインド
#include <iostream>
#include <string>
#include <boost/tr1/functional.hpp> 

using namespace std;

class Sandwitch {
  string mid_;
public:
  Sandwitch(const string& mid) : mid_(mid) {}
  string make(const string& head, const string& tail) const {
    return head + mid_ + tail;
  }
};

int main() {
  Sandwich s(" and ");
  Sandwich* p = &s;
  using namespace tr1::placeholders;
  // s.make("boys,"girls")
  cout << tr1::bind(&Sandwich::make, s, "boys", _1)("girls") << endl;
  // p->make("ladies","gentlemen")
  cout << tr1::bind(&Sandwich::make, p, "ladies", _1)("gentlemen") 
       << endl;
  // p->make("black","white")
  cout << tr1::bind(&Sandwich::make, p, _2, _1)("while","black") 
       << endl;
  // s.make("hit","away")
  cout << tr1::bind(&Sandwich::make, _1, "hit", "away")(s) << endl;
  // p->make("adam","eve");
  cout << tr1::bind(&Sandwich::make, _1, "adam", "eve")(p) << endl;
  // s.make("love","peace")
  cout << tr1::bind(&Sandwich::make, _1, _2, _3)(s,"love","peace") 
       << endl;

}
実行結果
boys and girls
ladies and gentlemen
black and while
hit and away
adam and eve
love and peace

  • LINEで送る
  • このエントリーをはてなブックマークに追加

修正履歴

  • 2008/02/15 08:46 「これまでの記事」項番を修正

著者プロフィール

  • επιστημη(エピステーメー)

    C++に首まで浸かったプログラマ。 Microsoft MVP, Visual C++ (2004.01~) だったり わんくま同盟でたまにセッションスピーカやったり 中国茶淹れてにわか茶人を気取ってたり、 あと Facebook とか。 著書: - STL標準講座 (監修) -...

バックナンバー

連載:BoostでC++0xのライブラリ「TR1」を先取りしよう

おすすめ記事

All contents copyright © 2006-2017 Shoeisha Co., Ltd. All rights reserved. ver.1.5