使用std :: function类成员进行编译器优化时出现段错误

时间:2019-03-27 22:37:41

标签: c++ gcc lambda c++17 gcc8

我有一个非常简单的代码示例,当在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的情况下不会出现此段错误。

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

问题: 我的示例中是否存在未定义的行为或错误的代码,或者可能是问题所在?

1 个答案:

答案 0 :(得分:6)

据我所知,这是不确定的行为。

问题是您的班级注册了引用

// ..........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,因此它创建了一个{strong>临时类型为{{ 1}},并以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)的引用,该对象可以保留到调用中。