为什么不能调用带有自动&;参数的常量可变lambda?
#include <type_traits>
int main()
{
auto f1 = [](auto&) mutable {};
static_assert(std::is_invocable_v<decltype(f1), int&>); // ok
auto const f2 = [](auto&) {};
static_assert(std::is_invocable_v<decltype(f2), int&>); // ok
auto const f3 = [](auto&) mutable {};
static_assert(std::is_invocable_v<decltype(f3), int&>); // failed
}
参见demo
为什么常量可变lambda不能接受引用参数?
解决方案
这里有两件有趣的事情。
首先,lambda的调用操作符(模板)默认为const
。如果您提供mutable
,则它不是const
。mutable
对lambda的影响仅与正常成员函数中的尾随const
的影响相反(它不影响lambda捕获等)
所以如果你看这个:
auto const f3 = [](auto&) mutable {};
static_assert(std::is_invocable_v<decltype(f3), int&>); // failed
这是一个const
对象,其调用操作符模板(因为它是一个泛型lambda)是非const
。因此您不能调用它,因为您不能在任何其他上下文中调用const
对象上的非const
成员函数。请参阅this other answer。
第二,有人指出,尽管如此,这是可行的:
auto const f4 = [](int&) mutable {}; // changed auto& to int&
static_assert(std::is_invocable_v<decltype(f4), int&>); // now ok
这是而不是编译器错误。也不是意味着我刚才说的是错的。f4
仍然有非常数调用运算符。您无法调用它,因为f4
是一个常量对象。
但是。
lambdas没有捕获的另一个有趣的方面是:它们有一个将函数转换为函数指针类型的函数。也就是说,我们通常认为lambdaf4
是这样的:
struct __unique_f4 {
auto operator()(int&) /* not const */ { }
};
如果这就是整个故事,const __unique_f4
确实不能用int&
调用。但它实际上看起来是这样的:
struct __unique_f4 {
auto operator()(int&) /* not const */ { }
// conversion function to the appropriate function
// pointer type
operator void(*)(int&)() const { /* ... */ }
};
我们有这样一个规则,当您调用一个对象时,例如f(x)
,您不仅要考虑f
的调用运算符--那些名为operator()
的成员--而且还要考虑f
的任何surrogate call functions--是否有任何函数指针可以转换为f
,然后调用。
在这种情况下,您可以!您可以将f4
转换为void(*)(int&)
,该函数指针可通过int&
调用。
但这仍然意味着f4
的调用运算符不是const,因为您声明了它是可变的。而且它没有说明您是否可以让mutable
lambdas获取引用参数。
相关文章