我终于觉得我理解了现代 C++ 中的 move 语义,并且它在我编写代码的方式上发生了巨大的变化。现在,我正在开发一个使用依赖注入(inject)的应用程序,我正在结合我对 move 语义的新知识,但我最终使用了太多的 std::move()
以至于我担心我使用不当。
以前,如果我想在我的对象中注入(inject)一个我需要一个拷贝的依赖项,我会这样编写我的构造函数:
class NeedsCopyOfFoo
{
public:
NeedsCopyOfFoo(const Foo& foo)
: m_myFoo{foo} {}
private:
Foo m_myFoo;
};
现在,我的类看起来像这样:
class NeedsCopyOfFoo
{
public:
NeedsCopyOfFoo(Foo foo)
: m_myFoo{std::move(foo)} {}
private:
Foo m_myFoo;
};
在我的设计中有一些类需要多达三四个类类型的依赖关系,我最终将它们全部 move 了。显然,如果我的构造函数的调用者无法使用右值调用构造函数,而且在构造 NeedsCopyOfFoo
对象后也不打算使用依赖项,我还需要使用 std::move()
在那里,以避免完全不必要的复制。
这就是现代 C++ 的样子吗? Bob 大叔是否提到了“过于频繁地使用 std::move()
”的代码味道?我是不是 react 过度了,因为我还不习惯用这种新风格写作?
最佳答案
TL;DR:如果您不关心完美的性能,那么
Class(const Foo& foo, const Bar& bar, ...) : m_myFoo{foo}, m_myBar{bar}, ...{...} {}
是你的构造函数。它需要右值/左值,并且会花费你一份拷贝。它是您所能得到的最好的东西,让生活变得轻松,拥有轻松的生活还有很多话要说。
对于一个变量,我会设置一个重载
NeedsCopyOfFoo(Foo&& foo) : m_myFoo{std::move(foo)} {}
NeedsCopyOfFoo(const Foo& foo) : m_myFoo{foo} {}
这最多花费一次复制或一次 move 操作,具体取决于传递给构造函数的对象类型。这是你能得到的最完美的。
不幸的是,这并不能很好地扩展。当您开始添加更多您想要以相同方式处理的参数时,过载集会呈二次方增长。这一点都不好玩,因为 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)} {}
这为您提供了最佳性能,但如您所见,它非常冗长,需要您了解更多有关 C++ 的知识才能使用。
关于c++ - 我是否经常使用 std::move() ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57923963/