【问题标题】:Why doesn't std::shared_ptr have operator->*?为什么 std::shared_ptr 没有 operator->*?
【发布时间】:2015-01-18 18:47:56
【问题描述】:

为什么 std::shared_ptr 没有operator->*?

使用可变参数模板似乎很容易实现。

请参阅this paper 了解更多信息。

编辑:这似乎是:About shared_ptr and pointer to member operator `->*` and `std::bind`的潜在副本

【问题讨论】:

  • @πάνταῥεῖ std::shared_pointer 可能,然而,持有一个类指针。然后很自然地能够将(sp->*ptr_to_member)() 写成(sp.get()->*ptr_to_member)() 的简写。不过,这种野兽的实现会很麻烦。
  • @πάνταῥεῖ 你的评论毫无意义?
  • @Taylor 我猜如果我们不需要点击链接,这个问题会更好
  • 这是一个有趣的问题,但我认为,不能期望 SO 可以肯定地回答。我的猜测是,在标准化过程中没有人想到或足够关心编写提案。
  • @Yakk 这也不适用于原始指针。

标签: c++ shared-ptr variadic-templates


【解决方案1】:

这可以在 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()-&gt;* 的理由。使用 lambda 而不是类也存在一些小缺陷,但这些可以修复。

克隆接口的解决方案也有缺陷(它们可以移动两次而不是一次,或者意味着指数级的重载)。

有趣的是我们可以在不修改std的情况下注入上面的-&gt;*:

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-&gt;* 将其考虑在内。有趣的是,-&gt;* 不需要是一个类的非静态成员,就可以像它的许多亲戚(如 -&gt; 和 [])那样使用它。

我为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&amp; args 替换 T args 来解决这个问题),但这很难。

相比之下,在 C++11 中这很容易。但是 std shared ptr 是与 C++11 一起设计的,它的前身是在它之前设计的。所以对于前身来说,添加-&gt;* 会很痛苦,而且回报很少,C++11 共享 ptr 就是基于这些编写的。

然而,这部分只是一个观点或一个故事。

【讨论】:

  • 一个使用示例不会有什么坏处:)
  • @light heck,我也可以让它免费:只要有一个 is_smart_ptr 特征。不确定使用示例包含哪些内容?将其注入智能 ptr 类,然后执行 (smart_ptr-&gt;*method_ptr)(arg1, arg2)?
  • 是的,为了 OP 的利益,也许值得展示一下!特别是您必须将其注入类的部分,这意味着这不会与std::shared_ptr一起工作,这就是问题所在。
  • @light 照明,加上不符合std 的版本,因为为什么不呢。仍然没有示例,因为这需要大于 4 英寸的屏幕,或者麻烦在我的手机上安装编译器。嗯,我应该这样做。
  • 我才意识到你没有回答这个问题。
【解决方案2】:

如果需要,可以自己添加。 operator -&gt;* 是一个ordinary binary operator,和operator + 一样,它是does not need 的成员。

这是一个更通用的版本,它将为任何实现operator -&gt; 的东西添加-&gt;* 支持。

template< typename obj, typename ptm >
auto operator ->* ( obj && o, ptm && p )
-> decltype( o.operator -> () ->* p )
    { return o.operator -> () ->* p; }

(推导的返回类型不合适,因为它不提供 SFINAE,然后模板可能会干扰其他重载。而且,是的,如果forwarded 右值,上述实现会更好。最后,如果不使用递归模板,则无法捕获 operator -&gt; 的 drill-down behavior。)

至于为什么shared_ptr 本身不提供它,指向成员的指针经常被遗忘。标准库的其他部分没有operator -&gt;*,尽管它总是适用于迭代器等。 Boost.SmartPtr 库中的功能也不是标准库功能之前的功能。 (Boost 通常有更多的花里胡哨。)

【讨论】:

  • 上述是否会递归调用-&gt;*,从而重复向下钻取,还是不会将自己视为-&gt;* 调用?
  • @Yakk 当时,我认为它不会,但现在我不明白为什么不这样做。最好试试看。
猜你喜欢
  • 2017-08-15
  • 1970-01-01
  • 2010-11-09
  • 2015-03-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-02
  • 1970-01-01
相关资源
最近更新 更多