std::move(*this) 是一个好的模式吗?

问题描述 投票:0回答:2

为了使带有 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&
,这不是预期的结果。

(似乎

*this
是隐式的,并且始终是左值引用,这是有道理的,但这是逃避使用
move
的唯一方法)

使用

clang 3.4
gcc 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
的情况以及同一函数的无
const
重载。在某些情况下,使用 const_cast(另一种形式的强制转换)来不重复代码并使两个函数具有相同的名称是有意义的 (
https://stackoverflow.com/a/124209/225186
)。 ...尽管在更复杂的情况下,使用辅助私有函数来委托接口函数的正确行为是有意义的。

c++ c++11 this move-semantics lvalue
2个回答
24
投票
*this

始终是左值,无论如何调用成员函数,因此如果您希望编译器将其视为右值,则需要使用

std::move
或等效项。考虑到这门课,它必须是:

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

*this

设置为右值意味着

fun
第一次调用
gun
可能会导致对象无法使用。第二次调用就会失败,而且可能会很严重。这不应该是隐式发生的事情。

这与在

void f(T&& t)

内部,

t
是左值的原因相同。在这方面,
*this
与任何参考函数参数没有什么不同。
    


0
投票
clone

模式的简单常见示例:

class Foo
{
  public:
    Foo() = default;
    Foo(Foo const&) = default;
    Foo(Foo&&) = default;
    virtual ~Foo() = default;
    virtual Foo* clone() const & = 0;
    virtual Foo* clone() && = 0;
    virtual void do_something() = 0;
};

class Bar : public Foo
{
  public:
    Bar() = default;
    Bar(Bar const&) = default;
    Bar(Bar&&) = default;
    virtual ~Bar() = default;
    Bar* clone() const & override { return new Bar(*this); }
    Bar* clone() && override { return new Bar(std::move(*this)); }
    void do_something() override { std::cout << "Hello !" << std::endl; }
}
 
void scoubidou(Foo&& f)
{
  std::unique_ptr<Foo> ff(std::move(f).clone());
  ff->do_something();
}

© www.soinside.com 2019 - 2024. All rights reserved.