This can be added to std::shared_ptr after C ++ 14 instead of the complex code you linked:
template<class Method> auto operator->*(Method&& method){ return [t=get(),m=std::forward<Method>(method)](auto&&args){ return (t->*m)(std::forward<decltype(args)>(args)...); }; }
adding the SFINAE option is optional. Pay attention to the above committed forward, which is imperfect. It also supports strange types of "methods" for a specific extension if they produce somethung with operator() and nothing important.
This is still imperfect due to the imperfection of the perfect forwarding, so it may be a reason to leave it alone and force .get()->* instead. There are also some minor flaws in using lambda instead of class, but they can be fixed.
Solutions that clone an interface also have flaws (they can move twice instead of one or imply an exponential number of congestion).
Funny we can enter above ->* without changing std :
namespace notstd{ template<class...Ts, class Method> auto operator->*(std::shared_ptr<Ts...> const& p, Method&& method){ return [t=p.get(),m=std::forward<Method>(method)](auto&&args){ return (t->*m)(std::forward<decltype(args)>(args)...); }; } template<class...Ts, class Method> auto operator->*(std::unique_ptr<Ts...> const& p, Method&& method){ return [t=p.get(),m=std::forward<Method>(method)](auto&&args){ return (t->*m)(std::forward<decltype(args)>(args)...); }; } }
then using notstd::operator->* takes this into account. Interestingly, ->* does not have to be a non-stationary member of a class for its use, unlike many of its relatives (for example, -> and [] ).
I have included a similar one for unique_ptr , because why not.
An alternative would save shared_ptr in the returned lambda: it adds overhead in what looks like a low-level operation, so I didnβt, and it would be bad for unique_ptr if it were funny.
Now everything said is good and good, but does not answer the question.
A C ++ 03 shared ptr (say boost shared ptr) could add:
template<class T, class R, class...Args> struct mem_fun_invoke; // details, exposes `R operator()(Args...)const` template<class T, class D, class R, class...Args> mem_fun_invoke<T,R,Args...> operator->*(std::shared_ptr<Ts...> const& p, R(T::*Method)(Args...)){ return mem_fun_invoke<T,R,Args...>(p.get(), Method); }
with ... emulated using macros (like boost ) or pattern code replication. It would not be ideal (two copies of each argument were made instead of one? I think we could replace args T with T const& args to fix this), but it would be difficult.
In comparison, in C ++ 11 it is simple. But std shared ptr was developed along with C ++ 11 and its predecessors that were developed before it. Thus, for the predecessors, adding ->* would be a lot of pain and a template for a small return, and C ++ 11 shared ptr was written based on these.
This part, however, is just an opinion or just that.