【问题标题】:Is std::move(*this) a good pattern?std::move(*this) 是一个好的模式吗?
【发布时间】:2014-10-09 16:16:08
【问题描述】:

为了使这个带有 C++11 引用限定符的代码按预期工作,我必须引入一个听起来不正确的 std::move(*this)

#include<iostream>
struct A{
    void gun() const&{std::cout << "gun const&" << std::endl;}
    void gun() &&{std::cout << "gun&&" << std::endl;}
    void fun() const&{gun();}
    void fun() &&{std::move(*this).gun();} // <-- is this correct? or is there a better option
};

int main(){
    A a; a.fun(); // prints gun const&
    A().fun(); // prints gun&&
}

这听起来有些不对劲。 std::move 有必要吗?这是推荐的用途吗? 目前,如果我不使用它,我在这两种情况下都会得到gun const&amp;,这不是预期的结果。

(似乎*this 总是隐含的左值引用,这是有道理的,但这是逃避使用move 的唯一方法)

clang 3.4gcc 4.8.3 测试。


编辑:这是我从@hvd 的回答中了解到的:

  1. std::move(*this) 在语法和概念上都是正确的

  2. 但是,如果gun 不是所需接口的一部分,则没有理由重载它的 lv-ref 和 rv-ref 版本。两个不同名字的函数可以做同样的工作。毕竟 ref-qualifiers 在接口级别很重要,这通常只是公共部分。

struct A{
private:
    void gun() const{std::cout << "gun const&" << std::endl;}
    void gun_rv(){std::cout << "gun called from fun&&" << std::endl;}
public:
    void fun() const&{gun();}
    void fun() &&{gun_rv();} // no need for `std::move(*this)`.
};

但同样,如果gun 是(通用)接口的一部分,那么std::move(*this) 是必要的,但仅此而已。而且,即使gun 不是接口的一部分,在不将函数gun 拆分为两个不同命名的函数时,可读性也有优势,而这样做的代价是……std::move(*this)

编辑 2:回想起来,这类似于 C++98 中 const 和 no-const 重载同一函数的情况。在某些情况中,使用const_cast(另一种形式的强制转换)来避免重复代码并让两个函数具有相同的名称(https://stackoverflow.com/a/124209/225186)是有意义的。 ...虽然在更复杂的情况下,使用辅助私有函数来委托接口函数的正确行为是有意义的。

【问题讨论】:

  • 如果你不想用std::move,那static_cast&lt;A&amp;&amp;&gt;(*this).gun()呢?
  • @yohjp,或者std::forward&lt;A&amp;&amp;&gt;(*this).gun()
  • 我建议创建一个名为 rvalue() 的成员函数,该函数将 *this 作为右值返回。然后你可以拨打this-&gt;rvalue()-&gt;...
  • @Mehrdad,会不会是这样写的函数auto rvalue()-&gt;decltype(std::move(*this)){return std::move(*this);}

标签: c++ c++11 this move-semantics lvalue


【解决方案1】:

是的,*this 始终是左值,无论成员函数如何调用,因此如果希望编译器将其视为右值,则需要使用std::move 或等效项。考虑到这个类,它必须是:

struct A {
  void gun() &; // leaves object usable
  void gun() &&; // makes object unusable

  void fun() && {
    gun();
    gun();
  }
};

*this 设为右值意味着fungun 的第一次调用会使对象无法使用。然后第二次调用将失败,可能很糟糕。这不应该隐含地发生。

这与void f(T&amp;&amp; t)t 内部是左值的原因相同。在这方面,*this 与任何引用函数参数没有什么不同。

【讨论】:

  • 是的,我明白了。那么std::move(*this) 是在调用链中使用所有&amp;&amp; 函数的方式吗? (我澄清了上面真正的问题)
  • @alfC 是的,就像其他函数参数一样。调用者指定对象可以安全地从中移出,但被调用函数本身也需要这样做,因为编译器无法自动计算出来。 (当然,由于this 是你自己的类类型,你也可以用不同的方式处理它:转发到一个可以在左值上调用的私有破坏性成员函数。由于它是私有的,外部用户不会意外调用它。)
  • 我不理解另一种方法,使用私有破坏性 (?) 成员。
  • 为了说明,示例中的第二个调用应该是std::move( * this ).gun();
  • @alfC void gun() &amp;&amp; 可以做的任何事情,void gun() 可以做。因此,如果您想调用void gun() &amp;&amp; 版本,您可以 做的是实现void gunRvalue(),即private,除非另有说明,否则调用左值通常不安全。您可以从public 函数中调用它,如下所示:void gun() &amp;&amp; { gunRvalue(); },以及您想要调用void gun() &amp;&amp; 版本的任何其他内容,可以直接调用gunRvalue()
猜你喜欢
  • 2015-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-22
  • 1970-01-01
相关资源
最近更新 更多