LoginSignup
2
2

More than 5 years have passed since last update.

std::unique_ptrには ->* 演算子がない!

Last updated at Posted at 2014-12-29

タイトルの通りです。
正直誰得です。

なんと、std::unique_ptrには->*演算子が定義されておらず、メンバ関数ポインタを使ってメソッドを呼び出すにはget()関数を挟む必要があります。これはいけない!! みんな困っている!!! これを解決しなければならない!!

std::unique_ptr < foo > pFoo( new foo() );
( pFoo.get()->*&foo::bar )(); // メンバ関数ポインタを使ってbarメソッドを呼び出す。

しかしこれでは直感的ではありませんし、そもそもどうしてもどこかへ生のポインタを渡したいときだけしかget()関数を使うべきではありません。そのためにアロー演算子もオーバーロードされていますしね。

そこで、->*演算子を足したunique_ptrを作ることにしました。

#include <functional> // for std::bind
#include <memory> // for std::unique_ptr

template < class Ty1, class Ty2 = std::default_delete < Ty1 > >
class unique_ptr
  : public std::unique_ptr < Ty1, Ty2 >
{
public:
  using base = std::unique_ptr < Ty1, Ty2 >;
  using base::unique_ptr;

  template < class ret, class... args >
  auto operator ->* ( ret( Ty1::*mem_ptr )( args... ) )
  {
    return std::bind( mem_ptr, *this->get() );
  }
};

std::unique_ptrをごっそり流用しているのでこの通り演算子だけを定義すれば済みます。
->*演算子はかなり特殊な演算子でそのまま関数の戻り値にはできない(値ではない)ので、std::bindで束縛したものを返しています。

unique_ptr < foo > pFoo( new foo() );
( pFoo->*&foo::bar )(); // 直感的

直感的かどうかは微妙なところ、というか->*演算子(間接メンバポインタというらしいです)自体使うことが滅多になく、存在も知らなかったという人もいそうなものなので直感的もへったくれもないかもしれません。
演算子の優先順位が低くてカッコでくくらなきゃいけないんですよね。

auto baz = &foo::bar; // 本当はこのように一度どこかへ代入されたり関数の引数を経たりする
( pFoo->*baz )(); // baz経由でbarメソッドを呼び出す

ここまで読んでいただいて恐縮なのですが、実はこれ、重大な欠陥がありまして、引数を取るものに対応していません。std::bindにプレースホルダーを置かないとならないからであり、それを解決していないからです。プレースホルダーはstd::placeholders::_1から_20まであり、最大20の引数に対応させることができます。テンプレートメタプログラミングを駆使すれば解決することができそうですが、割に合わない手間がかかりそうなのでやめました。ごめんなさい。

ネタ記事でした。

わたし自身あまりメンバ関数ポインタ周りは詳しくないので、間違い等ございましたらコメントのほうへお願い致します。

=> @kktk-KO さんより、C++14のラムダを使えば簡単に解決できることを教えてもらいました。ありがとうございます!

template < class Ty1, class Ty2 = std::default_delete < Ty1 > >
class unique_ptr
  : public std::unique_ptr < Ty1, Ty2 >
{
  /* 省略... */
  template < class Ret, class... Args >
  auto operator ->* ( Ret( Ty1::*mem_ptr )( Args... ) )
  {
    return [this , mem_ptr] ( Args&&... args )
    {
      return ( this->get()->*mem_ptr )( std::forward < Args... > ( args )... );
    };
  }
};
2
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2