请考虑以下代码:
#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;
}
此代码使用值填充向量,对其进行混洗,然后搜索指定值的索引(这只是一个示例来说明我的问题)。可以通过引用或lambda函数中的值来捕获此值cst
。
我的问题:两个版本之间的性能是否存在差异,还是编译器会以相同的方式对它们进行优化?
通过引用传递常量基本类型和通过引用传递常量类(如在普通函数中)是一个很好的规则吗?
答案 0 :(得分:6)
在实践中,小类型没有性能差异。
使用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; }
非const变量的引用捕获会导致非const unsigned&
引用。这会将原始值修改为副作用:
unsigned cst = 123;
[&](const int& i){ return i == ++cst; }
作为良好规则应避免复制大对象。如果小对象在lambda范围内应该是常量,但在当前范围内不是常量,则copy-capture是一个不错的选择。如果lambda的生命周期超过了本地对象的生命周期,则复制捕获是唯一的选择。
答案 1 :(得分:2)
lambda捕获并不真正相关。区别在于:
int x = y;
for (...)
if (x == z)
...
和
const int& x = y;
for (...)
if (x == z)
...
也就是说,存储对const int的引用vs对int的copy.of。第一个版本永远不会慢,但我认为优化器将设法为两者生成相同的代码。编译两个版本,并反汇编以查看会发生什么。
答案 2 :(得分:1)
cst
是unsigned int
因此不太可能有所作为。但是,如果您使用包含大量数据的大型类来执行此操作,则可能会有所不同,通过引用传递将更快。
在这种情况下要考虑的另一件事是在迭代向量时只复制一次对象。如果您看一下STL函数,大多数事情都是通过const引用或普通引用传递的,我不明白为什么捕获变量应该是任何不同的。虽然不幸的是你无法将变量捕获为const。
当然,在通过引用传递时总是要小心,因为你可以修改它,我认为在这种情况下,最好只传递一个const引用。
最后要考虑的是,由于编译器可能能够优化差异,您应该只使用您认为最佳指定意图的表单。所以基本上我同意你的假设,你应该
按值传递常量基本类型,按引用传递常量类