【问题标题】:Segfault when using std::function class member with compiler optimizations使用带有编译器优化的 std::function 类成员时出现段错误
【发布时间】:2019-03-27 22:37:41
【问题描述】:

我有一个非常简单的代码示例,在 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 级优化不应该破坏正确的代码。

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

【问题讨论】:

  • 将 lambda 传递给 const std::function&lt;...&gt; &amp; 参数会在现场创建一个临时的 std::function,然后在完整表达式结束时将其销毁。
  • std::function != lambda,它是一个包装器,这是一个性能成本

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


【解决方案1】:

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

问题是你的类注册了引用

// ..........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&lt;double()&gt; 类型的临时 对象,初始化为@987654331 @。

所以问题是:在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) 的引用,该对象在他的调用中仍然存在。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 2013-04-25
    • 1970-01-01
    相关资源
    最近更新 更多