C++標準ライブラリのスマートポインタ型std::shared_ptr<T>
では->*
演算子オーバーロードを提供しない(注:->
はあるよ)。
#include <memory> struct X { int mf(); }; // メンバ関数ポインタ int (X::*pmf)() = &X::mf; // (通常)ポインタ型 X* p = new X; p->mf(); // OK (p->*pmf)(); // OK // std::shared_ptr型 std::shared_ptr<X> sp{ p }; sp->mf(); // OK (sp->*pmf)(); // NG! (sp.get()->*pmf)(); // OK: get()でポインタを取出す (&*sp->*pmf)(); // OK: 暗号じみた記法
C++17で追加されたstd::invoke
関数を利用すると、メンバ関数ポインタ呼び出しであっても通常ポインタとスマートポインタ型を統一的に扱える。
#include <functional> std::invoke(pmf, p); // OK: X* std::invoke(pmf, sp); // OK: std::shared_ptr<X> // 代替(C++03/11/14) (*s.*pmf)(); // OK (*sp.*pmf)(); // OK
マイナー機能のため有用性は低いが、技術的には->*
演算子オーバーロードを提供可能。C++14機能までを使った実装例(エッセンスのみ):
// スマートポインタ型 template <typename T> struct SP { T* p_ = nullptr; SP(T* p): p_(p) {} // ->演算子オーバーロード T* operator->() { return p_; } // ->*演算子オーバーロード template<typename R, typename... Ts> auto operator->*(R (T::*pmf)(Ts...)) { return [p=p_, pmf](Ts&&... args) -> R { return (p->*pmf)(std::forward<Ts>(args)...); }; } }; // スマートポインタ型 SP<X> sp{ p }; sp->mf(); // OK (sp->*pmf)(); // OK
関連URL
- (PDF) Implementing operator->* for Smart Pointers
- cppreference invoke, cpprefjp invoke
- https://twitter.com/yohhoy/status/1549632914442551296