我是否经常使用std :: move()?

EDT*_*rdy 2 c++ move move-semantics c++17

我终于感觉到我了解现代C ++中的移动语义,并且它在编写代码的方式上发生了巨大的变化。现在,我正在开发一个使用依赖注入的应用程序,并结合了我对移动语义学的新发现,但是最终我使用了std::move()太多,以至于我担心自己使用不正确。

以前,如果要在对象中注入需要复制的依赖项,则可以这样编写我的构造函数:

class NeedsCopyOfFoo
{
public:
  NeedsCopyOfFoo(const Foo& foo)
    : m_myFoo{foo} {}
private:
  Foo m_myFoo;
};
Run Code Online (Sandbox Code Playgroud)

现在,我的课程如下所示:

class NeedsCopyOfFoo
{
public:
  NeedsCopyOfFoo(Foo foo)
    : m_myFoo{std::move(foo)} {}
private:
  Foo m_myFoo;
};
Run Code Online (Sandbox Code Playgroud)

我的设计中有一些类需要多达三到四个类类型的依赖关系,而最终我将它们全部移走了。显然,如果构造函数的调用者无法使用右值调用构造函数,但是在构造NeedsCopyOfFoo对象之后也将不使用依赖项,那么我也需要在此使用std::move()它,以避免完全不必要的复制。

这是Modern C ++应该看起来的样子吗?鲍勃叔叔是否提到“ std::move()经常使用” 的代码味道?我是否反应过度,因为我还不习惯这种新风格?

Nat*_*ica 9

TL; DR:如果您不希望获得完美的性能,那么

Class(const Foo& foo, const Bar& bar, ...) : m_myFoo{foo}, m_myBar{bar}, ...{...} {}
Run Code Online (Sandbox Code Playgroud)

是适合您的构造函数。它需要rvalues / lvalues,并且将花费您一个副本。它尽可能地使生活变得轻松愉快,而要过上轻松的生活有很多话要说。


对于仅一个变量,我将有一个重载集,例如

NeedsCopyOfFoo(Foo&& foo) : m_myFoo{std::move(foo)} {}
NeedsCopyOfFoo(const Foo& foo) : m_myFoo{foo} {}
Run Code Online (Sandbox Code Playgroud)

根据将哪种类型的对象传递给构造函数,此操作最多将花费一份副本或一次移动操作。这是您所能达到的最佳效果。

不幸的是,这不能很好地扩展。当您开始添加更多要以相同方式处理的参数时,过载集将平方增长。这根本不好玩,因为4参数构造函数需要16个重载才能完美。为了解决这个问题,我们可以使用转发构造函数并将其限制为SFINAE,以便仅采用所需的类型。那会给你一个像

template<typename T, 
         typename U, 
         std::enable_if_t<std::is_convertible_v<T, Foo> &&
                          std::is_convertible_v<U, Bar>, bool> = true>
Class(T&& foo, U&& bar) : 
    m_myFoo{std::forward<T>(foo)}, 
    m_myBar{std::forward<U>(bar)} {}
Run Code Online (Sandbox Code Playgroud)

这样可以使您获得最佳性能,但是正如您所看到的那样,它非常冗长,并且需要您更多地了解要使用的C ++。

  • 这些都是工具箱中的好工具。问题中也显示了传递价值的习语。诀窍是在正确的时间选择正确的工具。:-) (2认同)