【问题标题】:Why can't a const mutable lambda with an auto& parameter be invoked?为什么不能调用带有 auto& 参数的 const 可变 lambda?
【发布时间】:2022-01-15 10:13:08
【问题描述】:
#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

为什么 const 可变 lambda 不能接受引用参数?

【问题讨论】:

  • @DavisHerring 如果参数为int&amp;,则程序编译。如果参数为void,则程序编译。因此,它与论点有关。

标签: c++ lambda c++20 typetraits static-assert


【解决方案1】:

这里有两件有趣的事情。

首先,lambda 的调用运算符(模板)默认为const。如果您提供mutable,则它不是constmutable 对 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 still 有一个 non-const 调用运算符。您不能调用它,因为 f4 是一个 const 对象。

但是。

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

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

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

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

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

我们有一条规则,当你调用一个对象时,比如f(x),你不仅考虑f的调用操作符——那些名为operator()的成员——而且你还考虑任何@ 987654344@'s surrogate call functions -- 是否有任何函数指针可以将f 转换为,然后调用。

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

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

【讨论】:

  • 如果auto operator()(auto&amp;)? 似乎没有可用的函数指针转换函数
  • @S.M.仍然有转换函数运算符,只是它看起来像(对于f3 反正):template &lt;typename T&gt; operator void(*)(T&amp;) const。但是转换函数 templates 不是“代理调用函数”规则的一部分,只是转换函数 - 你可以在其他上下文中使用它们:auto const f3 = [](auto&amp;) mutable {}; void(*p)(int&amp;) = f3; // ok
【解决方案2】:

你会因为同样的原因得到一个错误:

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

int main() {
    const foo f;
    f();
}

错误是:

<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()(){}
         ^

因为您不能在 const 实例上调用非常量方法。 Lambdas 获得了默认的 constness 正确性,因此如果没有 mutableoperator() 就是 const。对于mutableoperator() 是一个非常量方法,您不能调用 const f3;

【讨论】:

  • 如果是这个原因,为什么将f3[](auto&amp;) mutable {}更改为[](int&amp;) mutable {}会使程序有效?
  • @S.M.不,operator() 绝对不是带有 mutable lambda 的 consteel.is/c++draft/expr.prim.lambda#closure-5.sentence-1
  • @Brian 对我来说就像一个错误
  • @S.M. mutable 不会使 lambda 成员可变。它使 lambda 成员非常量。
  • @463035818_is_not_a_number 为什么?你的答案是正确的。
猜你喜欢
  • 2019-07-23
  • 2015-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-23
  • 2017-08-28
相关资源
最近更新 更多