这可以在 C++14 之后添加到 std::shared_ptr,而不是您链接的复杂代码:
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)...);
};
}
添加 SFINAE 可选。注意上面的完美前锋,这是不完美的。它还在一定程度上支持奇怪的“方法”类型,只要它们产生带有operator() 且没有其他重要意义的东西。
由于完美转发的缺陷,这仍然是不完美的,因此这可能是一个不理会它并强制使用.get()->* 的理由。使用 lambda 而不是类也存在一些小缺陷,但这些可以修复。
克隆接口的解决方案也有缺陷(它们可以移动两次而不是一次,或者意味着指数级的重载)。
有趣的是我们可以在不修改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)...);
};
}
}
然后using notstd::operator->* 将其考虑在内。有趣的是,->* 不需要是一个类的非静态成员,就可以像它的许多亲戚(如 -> 和 [])那样使用它。
我为unique_ptr 加入了一个类似的,因为为什么不呢。
另一种选择是将 shared_ptr 存储在返回的 lambda 中:它在看起来像是低级操作的情况下增加了开销,所以我没有这样做,而在 unique_ptr 上,如果有趣的话,这是不明智的。
现在以上都很好,但没有回答问题。
可以添加一个 C++03 共享 ptr(比如 boost 共享 ptr):
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);
}
... 使用宏(如在 boost 中)或样板代码复制进行模拟。这并不完美(每个 arg 制作两个副本而不是一个?我想我们可以用 T const& args 替换 T args 来解决这个问题),但这很难。
相比之下,在 C++11 中这很容易。但是 std shared ptr 是与 C++11 一起设计的,它的前身是在它之前设计的。所以对于前身来说,添加->* 会很痛苦,而且回报很少,C++11 共享 ptr 就是基于这些编写的。
然而,这部分只是一个观点或一个故事。