什么样的代码需要注意rvalue引用?

时间:2010-11-13 17:27:34

标签: c++ c++11 rvalue-reference

我看到很多人在理解右值参考时遇到了问题。 “普通”C ++代码(例如使用标准库,但不实现它)是否需要在新标准出来时了解rvalue引用,或者人/代码可以假装右值引用不存在?右值引用主要涉及库实现者还是所有程序员?

8 个答案:

答案 0 :(得分:6)

我没有在GMan(又名gmannickg)关于这个主题的博客文章中看到任何不同意见:

http://blackninjagames.com/?p=95

总结:任何知道复制和交换(一般都是三条规则)的人,应该知道移动语义(以及因此rvalue引用),因为最佳实践习语会改变结果是。因此,我认为,任何人都关心C ++。

我不认为你知道,因为当然复制和交换仍然有效,它仍然是异常安全的,依此类推。我想有人可能会争论“普通”C ++代码和程序员是否需要知道复制和交换。我希望答案是“好吧,呃,显然”,但你永远不会知道。

当然,可以开始使用C ++ 0x的点因地而异。不久之后忘掉C ++ 03可能不是一个好主意。

例如,如果您可以依赖移动分配,则在对副本省略无效的上下文中按值返回大型集合会变得更好。这不是C ++ 03在C ++ 0x中变得有效的情况,它的代码在C ++ 03中非常慢,你可以解决这个问题(即使只有放置良好的swap),才能在C ++ 0x中变好。因此,如果你假装rvalue引用不存在,你将继续解决你不需要的东西。如果你进行了跳跃,那么它可能会干净地向C ++ 03后退,但是跑得很慢,这很痛苦。

所以聪明的事情可能是了解它,在许多情况下假装它不存在。

答案 1 :(得分:5)

至少在大多数情况下,只要您愿意,就可以忽略它们。 (重新)编写标准库以使用它们可以实现相当大的优化和一些新功能,但主要目标之一是现有代码应该继续正常工作(尽管在某些情况下,更快)。

支持现有代码意味着任何感觉它的人都可以继续以与以前相同的方式编写代码,并且不会受到影响(再次,除了他们使用的某些库“东西”可能更快)。

与此同时,有一些很好的理由,有人可能想要至少学习一些经验法则,让他们可以简单地使用rvalue-references。一个明显的例子是人们现在经常遇到的一个问题:他们有一个类,复制对象没有意义,但是他们仍然希望能够将这些对象放在一个集合中(例如,一个向量) )。

他们可以通过使该类的对象可移动但不可复制来实现。一个主要的例子就是流 - 现在,你不能将任何类型的iostream对象放入集合中,但是在C ++ 0x中你将能够。

另一种可能性是使用“完美转发”。这可以使得将以前已经将许多单独的功能合并到一个具有非常简单的前端的功能中变得更加容易,这些前端全部转发到执行实际工作的功能。你总是可以不这样做,但在某些情况下,它可以使代码重复性降低。

答案 2 :(得分:3)

大多数人可以假装他们不存在,他们只会看到好处(在某些情况下代码更快)。

请记住,C ++ 0x将向后兼容99.9%,因此所有现有代码仍然有效,这意味着您可以继续编写C ++ 0x代码,就好像它是C ++ 03一样。

如果你想获得右值参考的好处,那么当然你需要了解它们,但大多数时候这只是图书馆作家关注的问题。

答案 3 :(得分:3)

在编写一些带有一个函数的意义上,你可能不需要知道rvalue引用,但是你可以从理解移动语义,从知道移动构造函数是什么等方面受益。如果你想使用unique_ptr(并获得性能优势,因为它没有引用计数),那么你几乎肯定需要使用std :: move(比如说,进入和退出集合),我无法想象接近“如果我把一个&或*放在这里怎么办?”一个典型的二年级学生应对指针的方式。

我刚刚在柏林的Tech Ed Europe做了一个“VC ++中有新功能的C ++ 0x”谈话,其中有一个关于右值参考的幻灯片,我基本上说“你需要知道这些是因为他们制作了STL方式更快“但我没有进一步钻。参与者似乎比其他会谈中的(更长)变种更好地接受了。

答案 4 :(得分:2)

我同意, RValue References 不是每个人都喝茶 - 杯子很大。

正因为如此,我告诉C ++ 0x的学习者,开始并利用 RValue References 的最佳方法是使用STL并拥抱其设计模式随着时间的推移。很有可能,您可以从 RValue References 中受益,而无需事先完全理解它们。

答案 5 :(得分:1)

Scott Meyers将C ++ 0x功能划分为:

  1. 每个人的特点。
  2. 班级作者的特征。
  3. 图书馆作者的特色。
  4. RValues绝对是图书馆的作者。

答案 6 :(得分:1)

在c ++ 11中,我会通过&&来传递我计划“存储”或“消费”的任何内容。这导致了最有效的代码,但“打破”左值兼容性并迫使我考虑每个方法的两个(或更多)版本。不幸的是,你得到了rvalue和const& amp;的排列。随着参数的数量增加,它使得获取函数指针更加“难看”。

我没有提供两个(或更多)重载,一个用于左值,一个用于rvalue,我决定只提供rvalue重载,如果用户想要传递一个左值,它们用std :: ref(),std包装:: cref(),std :: move()或我自己的帮助器copy(),它使副本显式化并将左值转换为右值。 (std :: ref()和std :: cref()只有在参数是模板参数时才能工作......在这种情况下,该方法可能会使用std :: forward<>()来处理自动检测移动与复制语义。

template<typename T>
T copy( const T& v ) { return v; }

如果我正在编写一种不“存储”或“消耗”输入的方法,那么我使用const&amp;

我应该澄清一下,我会使用const&amp;对于任何不受移动语义影响的类型(无深拷贝)。但是当您编写模板代码时,您不希望对该类型做出该假设。

我正在考虑的另一种做法是让某些类只是“显式复制”。

class test  {
  public:
    test(){...};
    test( test&& t ){...}

  private:
    template<typename T>
    friend T copy(const T&t);

    test& operator=(const test&); // not implemented
    test( const test& t ){...}

    std::vector<char> large_data;
};

int main( int argc, char** argv ) {
  test x;
  test y = copy(x);
  test z = x; // error, test(const test&) is private...
  return 0;
}

我会将它用于任何包含任何“可移动成员”的类,例如字符串,向量,映射等。从技术上讲,我没有理由不能复制它们,但为什么我不应该尝试移动语义除非我真的想复制,否则无处不在?使用隐式副本,编译器无法警告我,我正在做一些可能很昂贵的事情。

答案 7 :(得分:0)

如果你没有实现一个value-emantics类,你真的不需要知道。

编辑:或完美转发。但这同样主要是图书馆使用。