因此,我在这里提出了一个问题:Lambda在最新的Visual Studio上运行,但是在我收到响应的其他地方都无法正常工作,因为标准25.1[algorithms.general] 10说:
除非另有说明,否则将以函数对象作为参数的算法允许自由复制那些函数对象。对于对象身份很重要的程序员,应考虑使用指向未复制的实现对象的包装器类,例如reference_wrapper<T>
reference_wrapper<T>
我只想知道为什么会这样吗?我们被告知一生要通过引用来获取对象,那么为什么标准要按值来获取函数对象,而在我链接的问题中,那些对象的副本更是如此?这样做是否有我不了解的优势?
std 假定函数对象和迭代器可以自由复制。
std
std::ref提供了一种将功能对象转换为伪引用的方法,该伪引用具有operator()使用引用而不是值语义的兼容性。因此,没有任何重大价值的损失。
std::ref
operator()
如果您一生都被教导要参考对象,请重新考虑。除非有充分的理由,否则请按价值来对待对象。关于价值的推理要容易得多;引用是指向程序中任何位置的任何状态的指针。
引用的常规用法是指向本地对象的指针,在使用该上下文的上下文中,任何其他活动引用都没有引用该引用,这不是阅读代码的人,也不是编译器可以假定的。如果您以这种方式推理引用,它们不会给您的代码增加可笑的数量。
但是,如果您以这种方式对它们进行推理,那么当您的假设被违反时,您将遇到错误,这些错误将是微妙的,严重的,意外的和可怕的。
一个经典的例子是operator=,当this和参数引用同一对象时,该中断的次数。但是,任何使用两个相同类型的引用或指针的函数都存在相同的问题。
operator=
this
但是,即使是一个引用也会破坏您的代码。让我们来看看sort。用伪代码:
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 )。
sort( begin(vector), end(vector), alice )
每次<调用时,所引用的order对象都会交换含义。现在,这是非常荒谬的,但是当你把Ordering通过const&,优化必须考虑到这种可能性,并排除它在你的订货代码每invokation!
<
order
Ordering
const&
您不会执行上述操作(实际上,该特定实现是UB,因为它违反了上的任何合理要求std::sort);但是编译器必须证明您ordering每次跟随order或调用它时都没有做“类似的事情”(更改中的代码)!这意味着不断地重新加载的状态order,或者内联并证明您没有做过这种疯狂。
std::sort
ordering
进行按值获取时,这样做要困难一个数量级(并且基本上需要类似的东西std::ref)。优化器具有一个函数对象,它是本地的,其状态是本地的。其中存储的所有内容都是本地的,编译器和优化器知道谁可以合法地对其进行修改。
您编写的每个使用a的函数const&都离开了其“本地范围”(例如,称为C库函数),不能假定const&其取回后的状态仍然相同。它必须从指针指向的任何地方重新加载数据。
现在,我确实说过按价值传递,除非有充分的理由。而且有很多很好的理由;例如,您的类型移动或复制非常昂贵,这是一个很大的原因。您正在向其中写入数据。您实际上希望每次阅读时都进行更改。等等。
但是默认行为应该是按值传递。仅在您有充分理由的情况下才转至引用,因为成本是分散的且难以确定。