-2

我一直在阅读有关右值引用和的内容std::move,并且我有一个性能问题。对于以下代码:

template<typename T>
void DoSomething()
{
  T x = foo();
  bar(std::move(x));
}

从性能的角度来看,使用右值引用重写它会更好吗?

template<typename T>
void DoSomething()
{
  T&& x = foo();
  bar(x);
}

bar(foo())如果我正确理解了右值引用,那么正如评论者所指出的那样,这将就像被调用一样。但可能需要中间值;这样做有用吗?

4

2 回答 2

4

在大多数情况下,使用右值引用/移动语义的最佳方式是不要太努力。按值返回函数局部变量。并通过这些函数的值构造它们:

X foo();

template<typename T>
void DoSomething()
{
  T x = foo();
  ...

如果您是 的作者class X,那么您可能希望确保X具有有效的移动构造和移动分配。如果拷贝构造函数和拷贝赋值X已经很有效,那么就没有什么可做的了:

class X
{
     int data_;
public:
     // copy members just copy data_
};

如果您发现自己在X的复制构造函数中分配内存,或者复制赋值,那么您应该考虑编写移动构造函数和移动赋值运算符。尽力制作它们noexcept

尽管规则总是有例外,但总的来说:

  1. 不要通过引用返回,无论是左值还是右值引用,除非您希望客户端能够直接访问非函数局部变量。

  2. 不要通过引用、左值或右值捕获返回。是的,在某些情况下,它可能会为您节省一些东西,而不会给您带来麻烦。甚至不要考虑将其作为一项规则。仅当您的性能测试高度激励您这样做时,才执行此类操作。

  3. 您(希望)学会的所有与左值引用无关的事情仍然与右值引用一样危险(例如从函数返回悬空引用)。

  4. 正确性的第一个程序。这包括编写广泛且易于运行的单元测试,涵盖 API 的常见和极端情况。 然后开始优化。当您开始尝试编写极其优化的代码(例如,通过引用捕获返回)之前,您还没有一个正确且经过良好测试的解决方案,一个人不可避免地会编写如此脆弱的代码,以至于出现的第一个错误修复只会进一步破坏代码,但通常以非常微妙的方式。这是一个即使是经验丰富的程序员(包括我自己)也必须一遍又一遍地学习的课程。

  5. 尽管我在 (4) 中说了些什么,即使在第一次写入时,也要注意 O(N) 性能。即,如果您的第一次尝试由于基本的整体设计缺陷或糟糕的算法而非常缓慢,以至于它无法在合理的时间内执行其基本功能,那么您需要的不仅仅是新代码,您还需要一个新设计。在这个项目符号中,我不是在谈论诸如您是否已通过右值引用捕获返回之类的事情。我说的是你是否创建了 O(N^2) 算法,当 O(N log N) 或 O(N) 可以完成这项工作。当需要完成的工作很重要,或者当代码非常复杂以至于你不知道发生了什么时,这一切都很容易做到。

于 2013-10-18T23:19:51.230 回答
1

在这里 std::move 没有帮助。
因为您已经制作了对象的副本(尽管省略可能会有所帮助)。

template<typename T>
void DoSomething()
{
  T x = foo();
  bar(std::move(x));
}

这里右值引用没有帮助
因为变量x是一个命名对象(因此不再是右值引用)。

template<typename T>
void DoSomething()
{
  T&& x = foo();
  bar(x);
}

最好使用:

template<typename T>
void DoSomething()
{
  bar(foo());
}

但是如果你必须在本地使用它:

template<typename T>
void DoSomething()
{
  T&& x = foo();
  //  Do stuff with x
  bar(std::move(x));
}

虽然我对上述内容不是 100% 肯定,但我希望得到一些反馈。

于 2013-10-18T23:21:26.957 回答