使用带有编译器优化的 std::function 类成员时出现 Segfault

Segfault when using std::function class member with compiler optimizations

本文关键字:function 成员 Segfault std 编译器 优化      更新时间:2023-10-16

我有一个非常简单的代码示例,当使用 gcc 8.2.0 下的-O2进行优化时崩溃

#include <vector>
#include <functional>
#include <iostream>
template<typename T, typename Container>
class Lambda_Expression
{
using Lambda = std::function<T()>;
const Lambda & _lambda;
public:
Lambda_Expression(const Lambda & l) : _lambda(l) {}
T operator[](const std::size_t i)
{
std::cerr << "inside expression [] " << i << std::endl;
return _lambda();
}
};
auto lambda = []() -> double
{
return 1.0;
};
int main()
{
int N = 10;
std::vector<double> res(N, 0.0);
double x = lambda();
std::cerr << "before for loop " << x << std::endl;
auto test_expression = Lambda_Expression<double, std::vector<double>>(lambda);
for( int idx=0; idx<N; ++idx )
{
std::cerr << "loop " << idx << std::endl;
double x = test_expression[idx];
}
}

也使用-std=c++17,以防万一。

我得到

before for loop 1
loop 0
inside expression [] 0
[1]    5288 segmentation fault  ./bench_lambdas

而我希望循环运行 10 次迭代。优化级别小于 2 时不会出现此段错误。

上面的例子对我来说似乎是相当无害的代码,据我所知,2 级优化不应该破坏正确的代码。

问题:我的示例中是否存在未定义的行为或不正确的代码,或者可能是什么问题?

据我所知,这是未定义的行为。

问题是您的类注册了引用

// ..........V  reference !!!
const Lambda & _lambda;

构造函数的参数

Lambda_Expression(const Lambda & l) : _lambda(l) {}

这是一个std::function

using Lambda = std::function<T()>;

但是当你用lambda调用构造函数时(如main()

)
auto test_expression = Lambda_Expression<double, std::vector<double>>(lambda);

_lambda保存对临时对象的引用lambda因为它不是std::function因此创建了一个临时对象,类型为std::function<double()>,使用lambda初始化。

所以问题来了:在构造结束时,对临时对象的引用变成了一个悬而未决的引用test_expression所以,当你调用test_expression[idx]时,你使用_lambda指向(可能)垃圾。

我建议避免此类问题,避免引用部分(使_lambda成为类型std::function的常规成员

const Lambda _lambda;  // <-- no more reference

所以你复制临时对象)

但是如果你真的想要_lambda是对std::function的引用,你应该写一些东西,

如下所示
std::function<double()>  f{lambda};
auto test_expression = Lambda_Expression<double, std::vector<double>>{f};

这样,构造函数接收到一个std::function对象(f)的引用,该对象在他的调用中幸存下来。