哪些类型的代码需要注意右值引用?

Posted

技术标签:

【中文标题】哪些类型的代码需要注意右值引用?【英文标题】:What kinds of code need to be aware of rvalue references? 【发布时间】:2010-11-13 17:27:33 【问题描述】:

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

【问题讨论】:

大部分情况下,CW 已不复存在。与其转移到 PSE.com,我会考虑稍微改写一下,比如“什么(种类的)代码将需要使用 rvaule-references?”不管措辞如何,我认为它确实非常直接地与代码和编程有关。 @John:“普通的 C++ 程序员”是说“模板类”的人,不能告诉 FTPS 是否重载,也没有听说过移动语义。那里有数以百万计的人。事实上,在 SO 上有成千上万的 here 我对这些事情有偏见并且天生脾气暴躁,但我一直认为没有 C++ 程序员不应该是“普通”的,因为他们不这样做真的不知道他们在做什么。任何黑客都可以通过敲击键盘来编写 Java 或 VB 程序。 C++ 程序员是——或者应该是——特别的。 @John:注意你写的是“应该”?我完全同意。 @John:我非常同意——但 C++ (IMO) 的真正优势之一是从初学者到专家的过渡相当平稳,不像某些语言(例如)保留一些功能开发语言本身的人。 【参考方案1】:

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

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

总结:任何了解复制和交换(以及通常的三规则)的人,应该了解移动语义(以及因此的右值引用),因为最佳实践习语会发生变化因此。因此,我认为任何人都关心 C++。

我认为您必须知道,因为当然复制和交换仍然有效,它仍然是异常安全的,等等。而且我想有人可能会争论“普通”C++代码和程序员是否甚至需要知道复制和交换。我希望答案是“嗯,嗯,很明显”,但你永远不会知道。

当然,开始使用 C++0x 的时间点因地而异。很快就忘记 C++03 可能不是一个好主意。

例如,如果您可以依赖移动分配,则在对复制省略无效的上下文中按值返回大量集合成为更好的选择。这不是在 C++03 中无效的东西在 C++0x 中有效的情况,而是在 C++03 中非常慢的代码,你可以解决(即使只有一个很好的 swap),在 C++0x 中变得很好。因此,如果您假装右值引用不存在,您将继续解决您不需要的事情。如果您进行跳转,那么它可能会干净地向后移植到 C++03,但运行速度很慢,这很痛苦。

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

【讨论】:

嗯,链接似乎无法修复。 GMan 的旧博客不见了,在我能找到的任何地方都没有存档。我会留下链接,以防它帮助任何人找到帖子。 @SteveJessop:啊,混蛋。你还记得这与什么有关吗? this 是正确的替代品吗? @GManNickG:我认为这是一个合理的替代品,但 IIRC(从近 2 年开始)与我最初链接的文章不同。我认为之前讨论的规则是 3 -> 规则 4,而这个是专门关于 operator= 和最后一个 C++11 位的复制和交换。 @SteveJessop:嗯,我不记得可能很糟糕。 :) 哦,好吧。 让我们假设你写了一篇非常好的文章来解决复制和交换问题,并证明 C++11 程序员会想知道如何使用右值引用来赋予他们的类移动语义。也许是我当时读的这篇文章,也许你也写了另一篇。也许两者兼而有之,而我现在只记得一半的那个不是我当时联系的那个!【参考方案2】:

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

支持现有代码意味着任何人都可以像以前一样继续编写代码,并且不会受到影响(同样,他们使用的某些库“东西”可能会更快)。

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

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

另一种可能性是使用“完美转发”。这可以使将以前的许多单独的功能合并为一个具有非常微不足道的前端的功能变得更加容易,这些前端都转发给真正的工作。你总是可以不这样做,但在某些情况下,它可以减少代码的重复性。

【讨论】:

【参考方案3】:

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

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

如果您想获得右值引用的好处,那么您当然需要了解它们,但大多数时候这只是库编写者关心的问题。

【讨论】:

【参考方案4】:

您可能不需要了解右值引用,因为您需要编写一些函数来接受一个,但您可能会从理解移动语义、了解什么是移动构造函数等中受益。如果您想使用 unique_ptr (并获得性能优势,因为它没有引用计数),那么您几乎肯定需要使用 std::move (例如,进出集合),我无法想象在“如果我在这里放一个 & 或 * 怎么办?”一个典型的二年级学生处理指针的方式。

我刚刚在柏林的 Tech Ed Europe 上做了一个“以 C++0x 为特色的 VC++ 中的新功能”演讲,其中有一张关于右值引用的幻灯片,我基本上说“你需要知道它们在这里,因为它们使 STL更快”,但我没有进一步钻探。这似乎比我在其他演讲中的(更长的)变体更能被与会者接受。

【讨论】:

我们能看到网络上某处谈话的幻灯片(甚至是视频)吗? 问题是所有那些“普通”程序员在你的幻灯片之后都知道右值引用是他们需要了解它们。 (我看过 Scott Meyers 对它们的全面介绍,我可以看到对于大多数 C++ 程序员来说,这将需要 非常 很长时间才能理解,即使是 Scott Meyers 介绍和完成的情况也是如此.) 询问,您将收到:msteched.com/2010/Europe/DEV311 具有低和高 WMV 和 ppts。我还没有看过视频,但是这个演讲在整个会议中被评为#4,所以很多人喜欢它。 @sbi - 他们可以在需要时深入研究。有些人会说“实际上我不介意 ref 计数开销”:-)。 @Kate:不,他们可以。可悲的是,许多人从来没有这样做过。如果他们不深入研究移动语义的全部内容,他们怎么知道什么时候需要理解它们?)【参考方案5】:

我同意,RValue 引用并不是每个人都喜欢喝茶——杯子很大。

因此,我告诉 C++0x 的学习者,入门和利用 RValue 引用的最佳方法是使用 STL 并接受它的设计模式强>随着时间的推移。很有可能您会从 RValue References 中受益,而无需事先完全理解它们。

【讨论】:

【参考方案6】:

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

    适合所有人的功能。 课程作者的功能。 图书馆作者的功能。

RValues 绝对是库的作者。

【讨论】:

我是从他的 C++0x 讲义中获取的,但是你让我再看一遍,我犯了一个错误:RValue 引用在“类作者的功能”和“...库作者”下【参考方案7】:

在 c++11 中,我将通过 && 传递我计划“存储”或“消费”的任何内容。这会产生最有效的代码,但会“破坏”左值兼容性,并迫使我考虑每种方法都有两个(或更多)版本。不幸的是,随着参数数量的增加,你会得到 rvalue 和 const& 排列的爆炸式增长,这使得获取函数指针更加“丑陋”。

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

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

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

我可能应该澄清一下,我会使用 const& 来处理任何不能从移动语义中受益的类型(没有深拷贝)。但是,当您编写模板代码时,您不想对类型做出这种假设。

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

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;

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

【讨论】:

【参考方案8】:

如果你没有实现一个值语义类,你真的不需要知道。

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

【讨论】:

以上是关于哪些类型的代码需要注意右值引用?的主要内容,如果未能解决你的问题,请参考以下文章

左值与右值引用 详解

右值引用的注意事项

重新理解C11的右值引用

C11新特性右值引用&&

转载右值引用

错误:对类型为'const ItemInstance'的引用无法绑定到类型为'void'的右值