2014年11月16日

C++ メモランダム (binder/adapter)

今日も寒い一日でした。11 月も半分が終わり、師走が近づいてくるとどうしても慌てだすんですよね。別に慌てる必要もないのに。

少し前に、STL のバインダ・アダプタを利用していて意味不明なエラーに遭遇し、ハマったことがあったので、ここで紹介しておきます。

次のようなクラスを作ったとします。

template<class T> struct OverloadTest
{
  void operator()( T& t1, T& t2 )
  { std::cout << t1 + t2 << std::endl; }

  void operator()( const T& t1, const T& t2 )
  { std::cout << t1 + t2 << std::endl; }
};

使うときは

OverloadTest<int> test;
int i = 5;
int j = 6;
test( i, j );

などと書けば、i + j = 11 を出力するという簡単なものです。ところが、

OverloadTest<int&> test;

と型を参照型にすると、

error: ‘void OverloadTest<T>::operator()(const T&, const T&) [with T = int&]’ cannot be overloaded
error: with ‘void OverloadTest<T>::operator()(T&, T&) [with T = int&]’

などとエラー表示されます。この場合は、メンバ関数をどちらか一方にすれば解決するし、そもそも二つのメンバ関数を定義しておく必要はありません( const T& の方を採用すればいいです )。

これと同じ現象が、STL の中の bind2nd と ptr_fun を組み合わせて使った時に発生しました。元々、構造体 RGB 用に用意した任意の演算子多重定義関数を、第二引数は固定値にして呼び出したいために作ったクラスです。

template<class T> class RGB_BindFunc : public RGB_Op
{
  typedef RGB& (*Op)( RGB&, T );

  std::binder2nd< std::pointer_to_binary_function<RGB&,T,RGB&> >
    ptrBinFunc_;

public:

  // PtrFunc コンストラクタ
  RGB_BindFunc( Op f, T t )
    : ptrBinFunc_( std::bind2nd( std::ptr_fun( f ), t ) ) {}

  // operator() : 任意の演算処理(override)
  virtual bool operator()( RGB& rgb )
  {
    ptrBinFunc_( rgb );
    return( true );
  }
};

これを次のように利用しようとすると、

RGB_BindFunc<unsigned char> func( (RGB& (*)( RGB&, unsigned char ))operator&=, 0x0F );

以下の様なエラーが出力されます。

.../binders.h:152: error: ‘typename _Operation::result_type std::binder2nd<_Operation>::operator()(typename _Operation::first_argument_type&) const [with _Operation = std::pointer_to_binary_function<GraphicLibrary::RGB&, unsigned char, GraphicLibrary::RGB&>]’ cannot be overloaded
.../binders.h:146: error: with ‘typename _Operation::result_type std::binder2nd<_Operation>::operator()(const typename _Operation::first_argument_type&) const [with _Operation = std::pointer_to_binary_function<GraphicLibrary::RGB&, unsigned char, GraphicLibrary::RGB&>]’

しばらくこのエラーに悩みましたが、binders.h の中でまさにさっき示したような定義がされていて、参照付きの変数が関数内にあるとうまくいかないということがわかったので、最終的に bind2nd と ptr_fun は使わずに組むことにしました。

Googleなんかで検索すると他にも悩んでいた方はいるようで、その中のアドバイスをいくつか参考にしました。しかし、ズバリこれで解決というようなものはなかったです。そもそもバグの類なのか、想定外の使い方をしてしまったのかも判別が付いてないです。