GPM*_*ler 3 c++ lambda gcc c++17 gcc8
我有一个非常简单的代码示例,当-O2在gcc 8.2.0下进行优化时会崩溃
#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];
}
}
Run Code Online (Sandbox Code Playgroud)
还要使用-std=c++17,以防产生影响。
我懂了
before for loop 1
loop 0
inside expression [] 0
[1] 5288 segmentation fault ./bench_lambdas
Run Code Online (Sandbox Code Playgroud)
而我希望循环能够运行10次迭代。优化级别低于2时,不会出现此段错误。
上面的示例对我来说看起来像是无害的代码,据我所知,第2级优化不应破坏正确的代码。
问题: 示例中是否存在未定义的行为或错误的代码,或者可能是问题所在?
据我所知,这是不确定的行为。
问题是您的班级注册了参考
// ..........V reference !!!
const Lambda & _lambda;
Run Code Online (Sandbox Code Playgroud)
构造函数的参数
Lambda_Expression(const Lambda & l) : _lambda(l) {}
Run Code Online (Sandbox Code Playgroud)
这是一个 std::function
using Lambda = std::function<T()>;
Run Code Online (Sandbox Code Playgroud)
但是,当您使用lambda调用构造函数时(如main())
auto test_expression = Lambda_Expression<double, std::vector<double>>(lambda);
Run Code Online (Sandbox Code Playgroud)
您将其保存在_lambda对临时对象的引用中,因为lambda不是std::function这样,所以它创建了一个类型为的临时对象,并使用进行了std::function<double()>初始化lambda。
因此出现了问题:在构造结束时,对临时对象的引用变成了悬空引用test_expression,当您调用时test_expression[idx],使用的_lambda是(潜在地)指向垃圾。
我建议避免此类问题,避免使用参考零件(使_lambda类型为常规成员)std::function
const Lambda _lambda; // <-- no more reference
Run Code Online (Sandbox Code Playgroud)
所以您复制临时对象)
但是,如果您确实希望这_lambda是对的引用std::function,则应编写如下内容
std::function<double()> f{lambda};
auto test_expression = Lambda_Expression<double, std::vector<double>>{f};
Run Code Online (Sandbox Code Playgroud)
通过这种方式,构造函数可以接收对std::function对象(f)的引用,该引用可以保留到调用中。