6

考虑以下代码:

#include <iostream>
#include <algorithm>
#include <numeric>

int main()
{
    const unsigned int size = 1000;
    std::vector<int> v(size);
    unsigned int cst = size/2;
    std::iota(v.begin(), v.end(), 0);
    std::random_shuffle(v.begin(), v.end());
    std::cout<<std::find_if(v.begin(), v.end(), [&cst](const int& i){return i == cst;})-v.begin()<<std::endl;
    std::cout<<std::find_if(v.begin(), v.end(), [=](const int& i){return i == cst;})-v.begin()<<std::endl;
    return 0;
}

这段代码用值填充一个向量,对其进行洗牌,然后搜索指定值的索引(这只是一个例子来说明我的问题)。该值cst可以通过引用或 lambda 函数中的值来捕获。

我的问题:两个版本之间的性能是否存在差异,或者编译器是否会以相同的方式对其进行优化?

通过值传递常量基本类型和通过引用传递常量类是一个好规则(就像在普通函数中一样)?

4

3 回答 3

7

实际上,小型类型没有性能差异。

clang -O3这两种情况下,我都会得到相同的代码。如果不进行优化clang,则会生成不同的代码,并且复制版本恰好是一条小指令。

$ clang ref.cpp -O3 -std=c++11 -S -o ref.s
$ clang cpy.cpp -O3 -std=c++11 -S -o cpy.s
$ diff ref.s cpy.s

与 const 相关的差异很小。

复制捕获为您提供了const unsigned价值。这不会编译:

unsigned cst = 123;
[=](const int& i){ return i == ++cst; }

非常量变量的引用捕获导致非常量unsigned&引用。这会修改原始值作为副作用:

unsigned cst = 123;
[&](const int& i){ return i == ++cst; }

作为一个好的规则,应该避免复制大对象。如果小对象在 lambda 的范围内应该是常量,但在当前范围内不是常量,则复制​​捕获是一个不错的选择。如果 lambda 的生命周期超过本地对象的生命周期,则复制捕获是唯一的选择。

于 2013-07-26T01:46:09.863 回答
2

lambda 捕获并不真正相关。区别在于:

int x = y;

for (...)
    if (x == z)
       ...

const int& x = y;

for (...)
    if (x == z)
       ...

也就是说,存储对 const int 的引用与获取 int 的副本。第一个版本永远不会变慢,但我认为优化器会设法为两者生成相同的代码。编译这两个版本,然后反汇编看看会发生什么。

于 2013-07-26T00:23:42.503 回答
1

cst是一个unsigned int,所以不太可能有所作为。但是,如果您对其中包含大量数据的大型类执行此操作,则可能会有所不同,通过引用传递会更快。

在这种情况下要考虑的另一件事是,在迭代向量时,对象只被复制一次。如果你看一下 STL 函数,大多数东西都是通过 const 引用或普通引用传递的,我不明白为什么捕获变量应该有任何不同。尽管不幸的是,您无法将变量捕获为 const。

当然,在通过引用传递时你总是要小心,因为你可以修改它,我认为在这种情况下,最好只作为 const 引用传递。

最后要考虑的一件事是,由于编译器可能能够优化差异,您可能应该只使用您认为最能说明您的意图的形式。所以基本上我同意你的假设,你应该

按值传递常量基本类型,按引用传递常量类

于 2013-07-26T00:17:35.263 回答