为什么不能调用带有 auto& 参数的 const 可变 lambda?

xml*_*lmx 29 c++ lambda static-assert type-traits c++20

#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
}
Run Code Online (Sandbox Code Playgroud)

查看演示

为什么 const mutable lambda 不能采用引用参数?

Bar*_*rry 33

这里有两件有趣的事情。

首先,lambda 的调用运算符(模板)是const默认的。如果你提供了mutable,那就不是constmutablelambda 对 lambda的影响与普通成员函数中尾随的影响完全const相反(它不影响 lambda 捕获等)

所以如果你看看这个:

auto const f3 = [](auto&) mutable {};
static_assert(std::is_invocable_v<decltype(f3), int&>); // failed
Run Code Online (Sandbox Code Playgroud)

这是一个const对象,其调用运算符模板(因为它是通用 lambda)是非const。所以你不能调用它,出于同样的原因,你不能在任何其他上下文中调用对象const上的非成员函数const。请参阅另一个答案

其次,有人指出,尽管如此,这是有效的:

auto const f4 = [](int&) mutable {}; // changed auto& to int&
static_assert(std::is_invocable_v<decltype(f4), int&>); // now ok
Run Code Online (Sandbox Code Playgroud)

这不是编译器错误。也不代表我刚才说的就是错的。f4 仍然有一个非常量调用运算符。您无法调用它,因为它f4是一个 const 对象。

然而。

没有捕获的 lambda 表达式还有另一个有趣的方面:​​它们有一个到函数指针类型的转换函数。也就是说,我们通常认为 lambdaf4看起来像这样:

struct __unique_f4 {
    auto operator()(int&) /* not const */ { }
};
Run Code Online (Sandbox Code Playgroud)

而且,如果这就是整个故事,const __unique_f4确实不能用 来调用int&。但实际上看起来是这样的:

struct __unique_f4 {
    auto operator()(int&) /* not const */ { }

    // conversion function to the appropriate function
    // pointer type
    operator void(*)(int&)() const { /* ... */ }
};
Run Code Online (Sandbox Code Playgroud)

我们有一条规则,当您调用一个对象时,例如f(x),您不仅考虑 的f调用运算符 - 那些命名的成员operator()- 而且您还考虑任何 的f代理调用函数- 是否有任何函数指针您可以转换f为,然后调用。

在这种情况下,你可以!您可以转换f4为 avoid(*)(int&)并且该函数指针可以使用 调用int&

但这仍然意味着f4's 调用运算符不是 const,因为您声明它是可变的。并且它没有说明是否可以让mutablelambda 接受引用参数。

  • @SM仍然有转换函数运算符,只是它看起来像(无论如何对于`f3`):`template &lt;typename T&gt; operator void(*)(T&amp;) const`。但是转换函数 _templates_ 不是“代理调用函数”规则的一部分,只是转换函数 - 您可以在其他上下文中使用它们:`auto const f3 = [](auto&amp;) mutable {}; void(*p)(int&amp;) = f3; // 好的` (4认同)

for*_*818 13

出于同样的原因,您会收到错误消息:

struct foo {
    void operator()(){}
};

int main() {
    const foo f;
    f();
}
Run Code Online (Sandbox Code Playgroud)

错误是:

<source>:7:5: error: no matching function for call to object of type 'const foo'
    f();
    ^
<source>:2:10: note: candidate function not viable: 'this' argument has type 'const foo', but method is not marked const
    void operator()(){}
         ^
Run Code Online (Sandbox Code Playgroud)

因为你不能在 const 实例上调用非常量方法。Lambdas的默认常量是正确的,所以没有mutableis 。是一个非常量方法,您不能调用operator()constmutableoperator()const f3;

  • @SM 不,“operator()”绝对不是带有“mutable”lambda 的“const”。http://eel.is/c++draft/expr.prim.lambda#closure-5.sentence-1 (6认同)
  • @SM mutable 不会使 lambda 成员可变。它使 lambda 成员成为非常量。 (4认同)
  • @463035818_is_not_a_number 为什么?你的答案是正确的。 (4认同)
  • 如果是这个原因,为什么将 `f3` 从 `[](auto&amp;) mutable {}` 更改为 `[](int&amp;) mutable {}` 会使程序有效? (2认同)