yohhoyの日記

2012-11-29

引数プレースホルダの衝突回避

C++11標準ライブラリとBoost.Bindライブラリが提供する、関数バインダ用の引数プレースホルダ(placeholders)に関するメモ。

C++標準ライブラリとBoost.Bindライブラリを(暗黙的に*1)併用する場合、_1 や _2 などの引数プレースホルダ名が衝突するケースがある。これはusingディレクティブによって名前空間std::placeholdersごと、またはusing宣言によって個別にstd::placeholders::_1など名前を取り込んだときに問題となる。

#include <functional>
using namespace std::placeholders;
#include <boost/bind.hpp>

int f(int, int);
auto g = std::bind(f, _1, 42);  // NG: '_1'が曖昧

上記コードでは両ライブラリがそれぞれ提供する識別子名 _1 が候補となり、両者の名前解決が曖昧であるためにコンパイル時エラーを引き起こす。

  • C++11: std::placeholders::_1
  • Boost*2: {anonymous}::_1

回避策としては、(1)マクロBOOST_BIND_NO_PLACEHOLDERSを定義してBoostライブラリ側の識別子名を無効化するか*3、(2)C++標準ライブラリ側の識別子名を修飾(qualified)して指定する方法がある。また、(3)バインダでは無くラムダ式の利用を検討する(→id:yohhoy:20120418)。

// (1)Boostライブラリ側の識別子名を定義しない
#define BOOST_BIND_NO_PLACEHOLDERS
#include /*boost/bind/bind.hppをincludeするヘッダ*/
//...
auto g = std::bind(f, _1, 42);

// (2)C++標準ライブラリ側の識別子名を明示指定する
auto g = std::bind(f, std::placeholders::_1, 42);

// (2')名前空間名の短い別名をつけて明示指定する
namespace ph = std::placeholders;
auto g = std::bind(f, ph::_1, 42);

// (3)C++ラムダ式の利用
auto g = [](int x){ return f(x, 42); }

関連URL

*1:Boost.Bindライブラリが提供する機能はC++11標準でも提供されるため、直接的に両ライブラリを併用することは無いはず。Boostライブラリ内での依存関係はhttp://hwada.hatenablog.com/entry/20110602/1307014311などを参照のこと。

*2:ここでは{anonymous}は無名名前空間(unnamed namespace)を表す。

*3:同マクロは“boost/bind/bind.hppヘッダ中からboost/bind/placeholders.hppヘッダをincludeする動作”を抑制するだけ。ユーザコードから直接boost/bind/placeholders.hppヘッダをincludeするケースには影響を与えない。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/yohhoy/20121129/p1
リンク元