在类分段错误中存储Lambda
Storing Lambda in class segfaults
这是我的程序的简化版本,以说明这个问题:
#include <functional>
#include <iostream>
template<class T>
class Piped {
public:
typedef T value_type;
};
template<class P1, class P2, class T>
class PipeConnector : public Piped<T> {
public:
PipeConnector(const P1 &p1, const P2 &p2)
: m_1(p1), m_2(p2) { }
bool run(const T &element) const {
return m_1.run(element) || m_2.run(element);
}
private:
const P1 &m_1;
const P2 &m_2;
};
template<class T>
class NullOp : public Piped<T> {
public:
bool run(const T&) const {
return false;
}
};
template<class T, class Functor>
class FunctionOp : public Piped<T> {
public:
FunctionOp(Functor f1)
: m_1(f1) { }
bool run(const T &element) const {
return m_1(element);
}
private:
std::function<bool(T)> m_1;
};
template<class P1, class Functor>
auto operator|(const P1 &p1, const Functor &f2) {
return PipeConnector<P1,
FunctionOp<typename P1::value_type, std::function<bool(typename P1::value_type)>>, typename P1::value_type>(
p1, FunctionOp<typename P1::value_type, std::function<bool(typename P1::value_type)>>(f2));
}
int main() {
auto p = NullOp<int>() | [](int x) -> bool { if (x < 10) { return true;} return false; };
std::cout << p.run(20) << std::endl;
return 0;
}
使用g++/clang++ -std=c++14编译这个程序会导致段错误。向它添加-O3,使它运行时没有段错误。
当将PipeConnector更改为不存储const引用而存储副本时,此操作有效。我认为问题是一些lambda作用域问题,但我不明白是什么出了问题。o3似乎忽略了这个问题?你能给我解释一下这个问题吗?
问题是NullOp<int>()
是在PipedConnector
中存储const-reference的临时对象。这个临时变量具有完整的表达式生存期,因此在p
初始化之后它不存在。当您调用p.run(20)
时,您将再次引用那个已被删除的临时对象。由此产生的UB可能导致崩溃。
您的operator|()
创建了一个PipeConnector
对象,其两个参数都是临时的。然后,PipeConnector
构造函数返回一个对象,其中包含对这些临时对象的引用。当调用该对象的run
方法时,临时对象的生存期已经过期。
NullOp
对象没有状态,它的引用是悬空的(在实践中)可能并不重要,但是由lambda构造的std::function
对象就不是这样了。这和本身不一样;lambda本身是无状态的,但是std::function
操作符包含一个函数指针。
我想使用-O3编译器可以找出在调用std::function
成员变量时应该调用什么函数,因此它内联调用。但它仍然是UB,即使它看起来有效。
Edit: OP建议延长临时对象的生存期,因为它绑定到一个引用。但是,在这种情况下,这并不适用。参见const引用是否会延长临时对象的寿命?
在调用operator|()
时创建的PipeConnector
的构造函数中有三个用临时对象初始化的引用。其中第一个是NullOp<int>()
,是operator|()
函数的参数,它的生命周期不会被§12.2 [class]的措辞显式地延长。[临时]第5.1段:
在函数调用(5.2.2)中绑定到引用形参的临时对象一直存在,直到包含该调用的完整表达式完成为止。
另外两个临时对象是std::function<bool(int)>
的实例(lambda
在FunctionOp<int, std::function<bool(int)>>
的初始化器中使用)和FunctionOp
本身的转换结果。在2013年和2014年上半年的标准草案版本中,这种情况(与函数调用情况非常相似)有类似的措辞:
构造函数的参数初始化器(12.6.2 [class.base.init])中的引用成员的临时绑定将一直存在,直到构造函数退出。
然而,这句话在DR 1696的拟议决议中被删除了(2014年9月29日提交给标准草案)。我相信删去这句话是为了清楚;据我所知,DR1696决议中的其他修改,特别是§12.6.2:
中的新第8段,都将上述情况标记为无效。在mem初始化式中绑定到引用成员的临时表达式是病态的。
- 如何调用存储在指向"std::函数"的指针中的 lambda?
- C++:对象将 lambda 存储到结构中,稍后调用该函数
- 是否可以将具有不同签名的 lambda 存储在 std::vector 中并在函数中执行它们(使用各自的参数)?
- 可以将模板化的 lambda 存储到 std::function 中吗?
- 将 lambda 存储为 std::函数时出现分段错误
- 在不模板化类的情况下存储 lambda
- 存储可变参数模板参数并将其传递给 lambda
- (C )将lambda函数存储在Boost Multi_Array中
- 带有动态存储持续时间的lambda
- Python -> C++ 习语:将 lambda 表达式存储在映射/容器中
- 存储lambda函数,该函数将范围变量捕获为类成员以供重用
- lambda 捕获的变量存储在哪里
- 将多种类型的 lambda 函数存储在一种类型的变量中
- C++ 存储在容器捕获"this"中的 Lambda
- 存储在std::函数中时,无法将无捕获lambda转换为函数指针
- 为什么我不能在SERVICE_TABLE_ENTRY中存储 lambda 函数?
- 如何在C++11中将lambda表达式存储为类的字段
- 将Lambda存储在类中
- 如何在类中存储lambda并将其捕获为参数
- 在类分段错误中存储Lambda