C++类中定义(static)lambda函数的编译错误及原理问询
问题代码与编译错误
初始代码(无法编译)
class MyClass { // for sorting auto lambda = [](const auto& x, const auto& y) { return x.x < y.x; }; // something that needs a custom way to sort std::set<SomeOtherClass, lambda> mySet; };
第一次编译报错
error: non-static data member declared with placeholder ‘auto’
auto databaseSortLambda = +[](const auto& x, const auto &y)
添加static修饰后的代码(仍无法编译)
class MyClass { // for sorting static auto lambda = [](const auto& x, const auto& y) { return x.x < y.x; }; // something that needs a custom way to sort std::set<SomeOtherClass, lambda> mySet; };
第二次编译报错
error: ‘constexpr’ needed for in-class initialization of static data member ‘MyClass::<lambda(const auto:1&, const auto:2&)> MyClass::lambda’ of non-integral type
疑问点
- 为何需要添加constexpr?我理解constexpr是让编译器在编译期计算表达式结果,但
x.x < y.x的结果需运行时确定,lambda怎么能是constexpr? - 编译器报错是否合理?如何在C++类中定义可作为std::set比较器的静态lambda?
解决方案与解释
关于constexpr的疑问
首先,constexpr修饰lambda不是要求lambda的执行结果在编译期确定,而是要求lambda本身作为一个对象,其构造(包括捕获内容、函数体等)是编译期可完成的。无捕获的lambda本身就是隐式constexpr的——因为它没有依赖运行时状态的内容,编译器可以在编译期就确定这个lambda的类型和实体。
C标准规定:非整数类型的静态成员变量在类内初始化时,必须用constexpr(或C20起的constinit)修饰,因为编译器需要在编译期完成该静态变量的初始化。lambda属于用户定义类型,显然是非整数类型,所以必须添加constexpr。
编译器报错的合理性与正确实现
编译器的报错完全符合C++标准。要在类中定义可作为std::set比较器的静态lambda,需要注意两个核心点:
- 静态lambda必须用
constexpr修饰,满足类内初始化的语法要求; std::set的模板参数需要的是类型,而lambda是一个具体对象,不能直接作为模板参数,必须通过decltype获取它的类型。
正确的实现代码如下:
class MyClass { // 用constexpr+static修饰无捕获lambda,满足类内初始化要求 static constexpr auto lambda = [](const auto& x, const auto& y) { return x.x < y.x; }; // 使用decltype获取lambda的类型,作为std::set的比较器模板参数 std::set<SomeOtherClass, decltype(lambda)> mySet{lambda}; };
如果需要将lambda的定义与声明分离,也可以采用类内声明、类外定义的方式:
class MyClass { // 类内声明静态constexpr lambda static constexpr auto lambda = [](const auto& x, const auto& y) -> bool; // 用decltype指定比较器类型 std::set<SomeOtherClass, decltype(lambda)> mySet; }; // 类外定义lambda(C++17及以上版本支持) constexpr auto MyClass::lambda = [](const auto& x, const auto& y) { return x.x < y.x; };
注意:上述代码需要编译器支持C17及以上标准(C17开始正式支持constexpr lambda)。
内容的提问来源于stack exchange,提问作者user2138149

