在这篇
talk中(对声音感到抱歉)Chandler Carruth建议在绝大多数情况下不要通过引用,甚至const引用,因为它限制了后端执行优化的方式.
他声称在大多数情况下副本可以忽略不计 – 我很高兴地相信,大多数数据结构/类等都在堆栈上分配了很小的部分 – 特别是与后端必须假设指针别名相比时可以对引用类型做的讨厌的事情.
假设我们在堆栈上有大对象 – 比如~4kB和一个对这个对象的实例做某事的函数(假设是独立函数).
经典我会写:
void DoSomething(ExpensiveType* inOut);
ExpensiveType data;
...
DoSomething(&data);
他建议:
ExpensiveType DoSomething(ExpensiveType in);
ExpensiveType data;
...
data = DoSomething(data);
根据我从谈话中得到的结果,第二种方法倾向于更好地进行优化.虽然我有多大这样的东西是有限制的,或者几乎在所有情况下后端拷贝省略的东西都会更喜欢这些值?
编辑:为了澄清我对整个系统感兴趣,因为我觉得这将是我编写代码的方式的一个重大改变,我已经使用refs而不是在我的内容中钻取的值大于整数类型的值很久了
编辑2:我也测试了它,结果和代码here.真的没有竞争,因为我们已经教了很长时间,指针是一种更快的做事方法.现在引起我兴趣的是为什么在那次谈话中我们提出了我们的价值,但是由于数字不支持它,所以我不打算这样做.
最佳答案 我现在看过钱德勒演讲的部分内容.我认为,“我现在应该总是通过价值”这句话的一般性讨论并不能说明他的谈话.编辑:实际上他之前已经讨论过他的演讲,这里是
value semantics vs output params with large data structures,还有来自Eric Niebler的博客,
http://ericniebler.com/2013/10/13/out-parameters-vs-move-semantics/.
回到钱德勒.在关键注释中,他特别提到(在其他地方提到的4x-5x分钟左右),提到了以下几点:
>如果优化器无法看到被调用函数的代码,则比传递ref或值有更大的问题.它几乎阻止了优化. (此时有一个关于链接时间优化的后续问题,我可能会在后面讨论,我不知道.)
>他推荐使用移动语义返回值的“新古典”方法.而不是旧学校方式将对现有对象的引用作为输入输出参数传递,而是应该在本地构造并移出值.最大的优点是优化器可以确保对象的任何部分都没有,因为只有函数可以访问它.
>他提到了线程,将变量的值存储在全局变量中,以及可观察的行为(如输出)作为未知数的示例,当仅传递refs /指针时,这些未知数会阻止优化.我认为抽象描述可能是“本地代码不能假设本地值更改在其他地方未被检测到,并且它不能假设本地未更改的值根本没有改变”.通过本地副本,可以做出这些假设.
显然,当按值传递(并且可能,如果对象无法移动,返回时),则在复制成本和优化优势之间存在折衷.大小和其他使复制成本高昂的东西会使参考策略达到平衡,而对函数中的对象进行大量优化工作会使其转向价值传递. (他的例子包括指向整数的指针,而不是指向4k大小的物体.)
根据我观察的部分,我认为钱德勒并不认为钱德勒是一种一刀切的策略.我认为他主要是在传递out参数而不是返回一个新对象的情况下通过引用来传递.他的例子不是关于修改现有对象的函数.
总的来说:
程序应该表达程序员的意图.如果您需要副本,请务必复制!如果要修改现有对象,请务必使用引用或指针.只有当副作用或运行时行为变得无法忍受时;真的只有尝试做一些聪明的事情.
还应该意识到编译器优化有时会令人惊讶.其他平台,编译器,编译选项,类库甚至只是对您自己的代码进行小的更改都可能会阻止编译器拯救.在许多情况下,变更的运行时成本完全出乎意料.