yohhoyの日記

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

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

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するケースには影響を与えない。