- 主题:lambda到底算不算variable?
template<class T>
void f(T&& a)
{
static_assert(std::is_rvalue_reference_v<decltype(a)>);
}
void g(int&& i)
{
}
int main()
{
auto test_lambda = [](){}
int i = 0;
auto test = [l{std::move(test_lambda)}, j{i}]()
{
f(std::move(l)); // pass compilation, pass static_assert;
g(std::move(j)); // fail, can't cast const int to int&&
}
}
l虽然也value-capture, 但是不受lambda的const限制,j确受到限制。查了很多地方没有说为什么,查了仔细都了lambda的标准,也没有明确说,唯一可以解释的地方就是"lambda is a closure",所以lambda和functor还是有区别。
--
FROM 60.191.0.*
我个人是把lambda当成一个常规情况下只读(不用mutable修饰的情况下)、同时提供了()运算符的对象来看待的
只不过这个对象不需要事先用class这样的类来规定里面的member,而是编译器根据capture列表,自行确定里面该放啥
实际上我感觉它可能就是一个提供了()运算符,同时内部有一个类似std::tuple的东西,capture的东西放在这个tuple里面,然后玩了点花样,让tuple里面的东西用和被capture的时候同样的名字被放问
【 在 ziqin 的大作中提到: 】
: template<class T>
: void f(T&& a)
: {
: ...................
--
FROM 113.120.108.*
跟chatgpt说半天车轱辘话,它给出的原因是l是值捕获,它捕获的是test_lambda的一个副本,这个副本是没有const属性的,所以下面的static_assert没问题:
template <class T> void f(T &&a) { static_assert(!std::is_const_v<decltype(a)>); }
--
FROM 221.220.171.*
搞清楚了,还是不能什么都信AI, 最后查了一堆生僻资料,发现 lambda move过去以后居然是一个const rvaluve reference,居然存在这种东西。 所以把g改成g(const int&&)就可以了。真是活久见,居然有const rvalue reference这种精神变态的定义。chatgpt关于副本是没有const属性是正确的,const属性是在operator() const上,但是结论错了
--
FROM 115.205.71.*
但是,事实上,const rvalue reference最后还是bind到const lvalue reference的ctor,所以即使是std::move过去的,也是一个copy
--
FROM 115.205.71.*
cppinsights里能看到实例化模板函数f(T&& a)后,a是一个const &&类型。
T&&比较特殊,是一个forward reference。
#ifdef INSIGHTS_USE_TEMPLATE
template<>
void f<const __lambda_20_24>(const __lambda_20_24 && a)
{
}
#endif
--
FROM 221.220.171.*
AI需要合适的hint/诱导
Q:
那么std::move(j)传给g(int &&)无法编译,但是传递给g(const int &&)却可以编译,是因为lambda默认是const的吗?
A:
抱歉之前的回答并不准确。这里,问题的核心其实在于 lambda 的默认行为和按值捕获的影响。
在 C++中,lambda 函数默认产生一个 const 成员函数,即它的operator()是一个const成员函数。这意味着,默认情况下,在lambda表达式的函数体内,所有捕获的值都被视为常量,无法被修改。这个行为与std::move产生的类型无关。
当你在一个 const 背景下尝试将一个按值捕获的局部变量传递给接受非const右值引用参数的函数时(例如 g(int&&)),编译器会拒绝,因为在 const 上下文中,你没有权限将一个变量作为非const右值引用传递。
另一方面,当你将参数更改为接受常量右值引用(例如 g(const int&&)),你告诉编译器该函数不会修改传递给它的参数。在 lambda 的 const 上下文中,这是可以接受的,因为即使 lambda 中的变量被视为 const,你仍然可以传递它们作为 const 右值引用,因为没有修改的操作涉及。这就是为什么添加 const 修饰符后代码可以编译的原因。
当声明一个 lambda 为 mutable 时,你可以修改按值捕获的变量,因为 lambda 的 operator() 不再是 const 的。这样,你就可以传递这些捕获的变量给接受非const右值引用的函数,例如 g(int&&)。
所以简而言之,lambda 默认是 const 的,这限制了你可以怎样使用按值捕获的变量。当尝试将捕获的非const值作为非const右值引用传递给函数时会造成编译错误,但将它们作为 const 右值引用传递是允许的。
【 在 ziqin 的大作中提到: 】
: 搞清楚了,还是不能什么都信AI, 最后查了一堆生僻资料,发现 lambda move过去以后居然是一个const rvaluve reference,居然存在这种东西。 所以把g改成g(const int&&)就可以了。真是活久见,居然有const rvalue reference这种精神变态的定义。chatgpt关于副本是没有const属性是正确的,const属性是在operator() const上,但是结论错了
--
FROM 221.220.171.*
你前面说的没问题,后面的std::tuple这个不一定对,看具体的编译器。
cppinsights上能看到clang编译器的内部表示
【 在 weiwallz 的大作中提到: 】
: 我个人是把lambda当成一个常规情况下只读(不用mutable修饰的情况下)、同时提供了()运算符的对象来看待的
: 只不过这个对象不需要事先用class这样的类来规定里面的member,而是编译器根据capture列表,自行确定里面该放啥
: 实际上我感觉它可能就是一个提供了()运算符,同时内部有一个类似std::tuple的东西,capture的东西放在这个tuple里面,然后玩了点花样,让tuple里面的东西用和被capture的时候同样的名字被放问
: ...................
--
修改:z16166 FROM 221.220.171.*
FROM 221.220.171.*
但是另一方面,加了mutable以后,lambda的调用会变成一个function pointer,不再有任何的关于inline的编译器优化,在性能方面会有所降低。chatgpt这种不同问法有不同答案的,真的还是需要自己在细节上再查下资料
【 在 z16166 的大作中提到: 】
: AI需要合适的hint/诱导
: Q:
: 那么std::move(j)传给g(int &&)无法编译,但是传递给g(const int &&)却可以编译,是因为lambda默认是const的吗?
: ...................
--
FROM 115.205.71.*
所有的模板库里,原来检测必须用move ctor的地方都是用 is_rvalue_reference_v,现在好了const rvalue reference一样能通过is_rvalue_reference_v检测,但是最后给我用copy ctor。吐血了,用same_as<std::decay_t<T>, T>重新检测了一下,结果一堆地方全是copy ctor
--
FROM 115.205.71.*