Nicolai Josuttis在他的书中The C++ Standard Library (Second Edition)
指出,编译器可以比普通函数更好地优化 lambda。
此外,C++ 编译器比普通函数更能优化 lambda。(第 213 页)
这是为什么?
我认为在内联方面应该不再有任何区别。我能想到的唯一原因是编译器可能有更好的本地上下文与 lambda,这样可以做出更多假设并执行更多优化。
Nicolai Josuttis在他的书中The C++ Standard Library (Second Edition)
指出,编译器可以比普通函数更好地优化 lambda。
此外,C++ 编译器比普通函数更能优化 lambda。(第 213 页)
这是为什么?
我认为在内联方面应该不再有任何区别。我能想到的唯一原因是编译器可能有更好的本地上下文与 lambda,这样可以做出更多假设并执行更多优化。
原因是 lambda 是函数对象,因此将它们传递给函数模板将专门为该对象实例化一个新函数。因此,编译器可以简单地内联 lambda 调用。
另一方面,对于函数,旧的警告适用:函数指针被传递给函数模板,编译器传统上通过函数指针内联调用存在很多问题。它们理论上可以内联,但前提是周围的函数也内联。
例如,考虑以下函数模板:
template <typename Iter, typename F>
void map(Iter begin, Iter end, F f) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
用这样的 lambda 调用它:
int a[] = { 1, 2, 3, 4 };
map(begin(a), end(a), [](int n) { return n * 2; });
此实例化的结果(由编译器创建):
template <>
void map<int*, _some_lambda_type>(int* begin, int* end, _some_lambda_type f) {
for (; begin != end; ++begin)
*begin = f.operator()(*begin);
}
…编译器知道_some_lambda_type::operator ()
并且可以简单地内联对它的调用。map
(并且使用任何其他 lambda调用该函数将创建一个新的实例化 ,map
因为每个 lambda 都有不同的类型。)
但是当使用函数指针调用时,实例化如下所示:
template <>
void map<int*, int (*)(int)>(int* begin, int* end, int (*f)(int)) {
for (; begin != end; ++begin)
*begin = f(*begin);
}
…这里f
为每个调用指向一个不同的地址,map
因此编译器不能内联调用,f
除非周围的调用map
也被内联,以便编译器可以解析f
到一个特定的函数。
因为当您将“函数”传递给算法时,您实际上是在传递指向函数的指针,因此它必须通过指向函数的指针进行间接调用。当您使用 lambda 时,您将一个对象传递给专门为该类型实例化的模板实例,并且对 lambda 函数的调用是直接调用,而不是通过函数指针的调用,因此更有可能是内联的。
Lambda 并不比通常的函数快或慢。如有错误请指正。
首先,lambda和普通函数有什么区别:
让我们谈谈捕获。它不会给函数带来任何性能,因为编译器必须传递额外的对象以及处理捕获所需的数据。无论如何,如果您只使用 lambda 函数,它将很容易优化。此外,如果 lambda 不使用捕获,您可以将 lambda 转换为函数指针。为什么?因为如果它没有捕获它只是一个普通的功能。
void (*a1)() = []() {
// ...
};
void _tmp() {
// ...
}
void (*a2)() = _tmp;
上面的两个例子都是有效的。
谈到从目标文件中删除功能。你可以简单地将你的函数放到匿名命名空间中,它就会达成交易。内联函数会更高兴一点,因为除了您的文件之外,它不会在任何地方使用。
auto a1 = []() {
// ...
};
namespace {
auto a2() {
// ...
}
}
上述功能将与性能相同。
我还注意到比较了函数指针和 lambda。这不是一件好事,因为它们是不同的。当你有一个指向函数的指针时,它可以指向各种不同的函数,并且它可以在运行时改变,因为它只是一个指向内存的指针。Lambda 无法做到这一点。它总是只使用一个函数,因为调用哪个函数的信息存储在类型本身中。
您可以使用这样的函数指针编写代码:
void f1() {
// ...
}
void f2() {
// ...
}
int main() {
void (*a)();
a = f1;
a = f2;
}
绝对没问题。而且您不能以这种方式使用 lambdas 编写代码:
int main() {
auto f1 = []() {
// ...
};
auto f2 = []() {
// ...
};
f2 = f1; // error: no viable overloaded '='
}
如果某些库接受函数指针,这并不意味着编译器可以比普通函数更好地优化 lambda,因为问题不在于通用库和函数指针。