一尘不染

标准算法是否有理由重视Lambda?

algorithm

因此,我在这里提出了一个问题:Lambda在最新的Visual
Studio上运行,但是在我收到响应的其他地方都无法正常工作,因为标准25.1[algorithms.general] 10说:

除非另有说明,否则将以函数对象作为参数的算法允许自由复制那些函数对象。对于对象身份很重要的程序员,应考虑使用指向未复制的实现对象的包装器类,例如reference_wrapper<T>

我只想知道为什么会这样吗?我们被告知一生要通过引用来获取对象,那么为什么标准要按值来获取函数对象,而在我链接的问题中,那些对象的副本更是如此?这样做是否有我不了解的优势?


阅读 264

收藏
2020-07-28

共1个答案

一尘不染

std 假定函数对象和迭代器可以自由复制。

std::ref提供了一种将功能对象转换为伪引用的方法,该伪引用具有operator()使用引用而不是值语义的兼容性。因此,没有任何重大价值的损失。

如果您一生都被教导要参考对象,请重新考虑。除非有充分的理由,否则请按价值来对待对象。关于价值的推理要容易得多;引用是指向程序中任何位置的任何状态的指针。

引用的常规用法是指向本地对象的指针,在使用该上下文的上下文中,任何其他活动引用都没有引用该引用,这不是阅读代码的人,也不是编译器可以假定的。如果您以这种方式推理引用,它们不会给您的代码增加可笑的数量。

但是,如果您以这种方式对它们进行推理,那么当您的假设被违反时,您将遇到错误,这些错误将是微妙的,严重的,意外的和可怕的。

一个经典的例子是operator=,当this和参数引用同一对象时,该中断的次数。但是,任何使用两个相同类型的引用或指针的函数都存在相同的问题。

但是,即使是一个引用也会破坏您的代码。让我们来看看sort。用伪代码:

void sort( Iterator start, Iterator end, Ordering order )

现在,让订购成为参考:

void sort( Iterator start, Iterator end, Ordering const& order )

这个怎么样?

std::function< void(int, int) > alice;
std::function< void(int, int) > bob;
alice = [&]( int x, int y ) { std:swap(alice, bob); return x<y; };
bob = [&]( int x, int y ) { std:swap(alice, bob); return x>y; };

现在,致电sort( begin(vector), end(vector), alice )

每次<调用时,所引用的order对象都会交换含义。现在,这是非常荒谬的,但是当你把Ordering通过const&,优化必须考虑到这种可能性,并排除它在你的订货代码每invokation!

您不会执行上述操作(实际上,该特定实现是UB,因为它违反了上的任何合理要求std::sort);但是编译器必须证明您ordering每次跟随order或调用它时都没有做“类似的事情”(更改中的代码)!这意味着不断地重新加载的状态order,或者内联并证明您没有做过这种疯狂。

进行按值获取时,这样做要困难一个数量级(并且基本上需要类似的东西std::ref)。优化器具有一个函数对象,它是本地的,其状态是本地的。其中存储的所有内容都是本地的,编译器和优化器知道谁可以合法地对其进行修改。

您编写的每个使用a的函数const&都离开了其“本地范围”(例如,称为C库函数),不能假定const&其取回后的状态仍然相同。它必须从指针指向的任何地方重新加载数据。

现在,我确实说过按价值传递,除非有充分的理由。而且有很多很好的理由;例如,您的类型移动或复制非常昂贵,这是一个很大的原因。您正在向其中写入数据。您实际上希望每次阅读时都进行更改。等等。

但是默认行为应该是按值传递。仅在您有充分理由的情况下才转至引用,因为成本是分散的且难以确定。

2020-07-28