标准算法按值获取 lambda 是否有原因?



所以我在这里问了一个问题: Lambda适用于最新的Visual Studio,但在其他地方不起作用 我得到的回应是,我的代码是自标准的25.1 [algorithms.general] 10以来定义的实现 说:

除非另有说明,否则允许复制将函数对象作为参数的算法 这些函数对象自由。对象标识很重要的程序员应考虑使用 指向未复制的实现对象(如reference_wrapper<T>

我只是想知道为什么会发生这种情况?我们一生都被告知要通过引用来获取对象,那么为什么标准要按值获取函数对象,甚至更糟的是在我的链接问题中复制这些对象?这样做有什么我不明白的好处吗?

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

std::ref提供了一种将函数对象转换为具有使用引用而不是值语义的兼容operator()的伪引用的方法。 因此,不会丢失任何大价值的东西。

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

引用的传统用法,作为指向本地对象的指针,该对象在使用它的上下文中未被任何其他活动引用引用,这不是阅读您的代码的人或编译器可以假定的。 如果您以这种方式推理引用,它们不会为您的代码增加荒谬的复杂性。

但是,如果你以这种方式推理它们,当你的假设被违反时,你就会有错误,它们将是微妙的、粗暴的、出乎意料的和可怕的。

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

但即使是一个引用也会破坏您的代码。 让我们看看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对象都会交换含义。 现在这很荒谬,但是当你通过const&Ordering时,优化器必须考虑到这种可能性,并在每次调用您的订购代码时排除它!

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

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

你编写的每个函数都采用离开其"本地范围"(例如,称为 C 库函数)的const&,不能假设const&的状态在返回后保持不变。 它必须从指针指向的任何位置重新加载数据。

现在,我确实说过除非有充分的理由,否则按值传递。 有很多很好的理由;例如,您的类型移动或复制非常昂贵,这是一个很好的原因。 您正在向其写入数据。 您实际上希望每次阅读时它都会发生变化。 等。

但默认行为应为按值传递。 只有在有充分理由的情况下才转向参考,因为成本是分散的,很难确定。

我不确定我能给你一个答案,但是如果我的对象生存期是正确的,我认为这是可移植的、安全的,并且不会增加开销或复杂性:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>

// @pre f must be an r-value reference - i.e. a temporary
template<class F>
auto resist_copies(F &&f) {
return std::reference_wrapper<F>(f);
};
void removeIntervals(std::vector<double> &values, const std::vector<std::pair<int, int>> &intervals) {
values.resize(distance(
begin(values),
std::remove_if(begin(values), end(values),
resist_copies([i = 0U, it = cbegin(intervals), end = cend(intervals)](const auto&) mutable 
{
return it != end && ++i > it->first && (i <= it->second || (++it, true));
}))));
}

int main(int argc, char **args) {
// Intervals of indices I have to remove from values
std::vector<std::pair<int, int>> intervals = {{1,  3},
{7,  9},
{13, 13}};
// Vector of arbitrary values.
std::vector<double> values = {4.2, 6.4, 2.3, 3.4, 9.1, 2.3, 0.6, 1.2, 0.3, 0.4, 6.4, 3.6, 1.4, 2.5, 7.5};
removeIntervals(values, intervals);
// intervals should contain 4.2,9.1,2.3,0.6,6.4,3.6,1.4,7.5
std:
copy(values.begin(), values.end(), std::ostream_iterator<double>(std::cout, ", "));
std::cout << 'n';
}

相关内容

最新更新