【问题标题】:Generic lambdas: syntactic sugar or not?通用 lambda:语法糖与否?
【发布时间】:2023-03-29 15:27:01
【问题描述】:

C++14 泛型 lambda 是否对语言带来了真正的改进,或者它们是一种语法糖?是否有一些情况

[](auto param1, auto param2, /* ... */ auto paramN)
{
    return /* ... */;
}

不能替换为

template <typename Type1, typename Type2, /* ... */ typename TypeN>
auto foo(Type1&& param1, Type2&& param2, /* ... */ TypeN&& paramN)
{
    return  /* ... */;
}

struct bar
{
    template <typename Type1, typename Type2, /* ... */ typename TypeN>
    auto operator()(Type1&& param1, Type2&& param2, /* ... */ TypeN&& paramN)
    {
        return  /* ... */;
    }
};

?


@Kerrek SB 提供了非常有趣的链接 in the comments,它们说明了通用 lambda 的强大功能:

【问题讨论】:

  • 你可以问同样的非泛型 lambdas。
  • 原来lambda是语法糖;为了更容易>o
  • 您的问题表明语法糖永远不会给语言带来任何真正的改进。我强烈不同意这一点。
  • @Constructor 该标准已经要求编译器将 lambda(通用或非通用)替换为具有operator() 成员函数的自动生成的类。如果编译器可以做到,我想不出任何你自己不能做到的情况。 (但我最近看到了 lambda 的一些非常有创意的用法,这些用法可以实现我认为不可能的事情,所以我还不能完全确定。)

标签: c++ lambda syntactic-sugar c++14 generic-lambda


【解决方案1】:

对于 C++11 的非泛型 lambda 表达式,可以执行一些简单的翻译:

void foo()
{
    int i = 42; int j = 22;
    auto f = [i, &j](int k) { return i + j + k };
    // proceed to use f
 }

例如:

void foo()
{
    int i = 42; int j = 22;
    struct {
        int i; int& j;
        // can't deduce return type
        int operator()(int k) const
        { return i + j + k; }
    } f { i, j };
    // proceed to use f
}

对于 C++14 的通用 lambda 表达式,它并不是那么简单。假设这次我们使用auto f = [i, &amp;j](auto k) { return i + j + k; }。然后我们必须产生以下调用运算符:

template<typename T>
auto operator()(T k) const { return i + j + k; }

问题是我们不能在函数范围内定义模板(限制也称为没有本地模板)。所以我们必须将闭包类型定义从封闭函数移出到命名空间范围(在过程中给它一个名字),然后使用closure_type f { i, j };。顺便说一句,这意味着我们必须给类及其运算符某种形式的链接,而函数本地定义没有链接。

所以从某种意义上说,通用 lambda 表达式为我们提供了有限版本的本地函数模板。

【讨论】:

  • 非常感谢!你认为泛型 lambda 和模板化 operator() 的闭包类之间没有其他区别吗?
  • @Constructor 一个闭包类型(从 lambda 表达式产生)需要像这样的类。这实际上意味着像auto f = [] {}; auto ptm = &amp;decltype(f)::operator(); 这样的东西需要工作——在这方面没有任何改变。
  • 嗯,it works even for generic lambda。但是怎么做呢?
  • 哦,当然不行(我的意思是我的代码)。 coliru 今天有点奇怪。是的,我知道这个技巧,谢谢。
【解决方案2】:

关于一般的 Lambda:

有些人认为这“非常整洁!”;其他人将其视为编写危险晦涩代码的一种方式。 IMO,两者都是正确的。 --- Bjarne Stroustrup

我认为这是你如何使用 lambda 的问题。作为小型局部闭包函数,您可以使用它来改进函数的处理,将函数对象作为参数(如std::sort),我实际上还没有看到通用 lambda 可以增加任何好处的示例。

如果您使用它们在 C++ 中编写类似 haskell 的代码,那么它会增加一些好处,但是我已经看到太多的代码示例,其中至少部分忽略了对象生命周期。所以我不认为它会增加好处。

让我解释一下:

void foo(std::vector<int>& v)
{
    std::sort(v.begin(), v.end(), [](int i, int j) { return (i < j); });
}

这是一个真正的改进,但注册了一个回调,稍后将调用它,并且可能在另一个线程中调用:

void C::foo()
{
    registerLazyMultithreadedCallback([=this]() { return this; });
}

这会使事情变得复杂,因为您必须确保返回的对象是有效的。这可能会导致荒谬的情况(例如,在销毁后短时间内调用)。我的经验是,在编写这样一个没有 lambda 的构造之前,编码人员会三思而后行。

所以如果你只在本地使用它们作为辅助函数,就不需要泛型,因为所有类型都是明确的。

我想我是那些认为你可以用 lambda 编写危险的晦涩代码的人之一。

【讨论】:

  • 您的答案是关于简单的 lambda,而不是通用的。
  • 这是关于 lambdas 的使用。如果在本地使用辅助函数,就不需要泛型编程,因为类型很明确...
  • 另外,您所抱怨的同样适用于手动编码的函数或仿函数,因此 lambda 不应该为此负责。在正确使用它们的情况下,它们可以正常工作(即相同)并且看起来更具可读性。不使用 lambda 也可以编写危险的晦涩代码,如果这样做,它会占用两倍的行数。
【解决方案3】:

C++14 通用 lambda 是否为语言带来了真正的改进

是的。

或者它们是一种语法糖?

是的。

您似乎暗示语法糖并不是对语言的真正改进。请记住,语言本身就是语法糖。您可以用机器代码编写所有内容:)

【讨论】:

  • 不,不。我同意它们是一个真正的改进。我用错了词。但是问题的第二部分呢?
  • 造成问题的不是语法糖,而是开发人员无法正确处理对象生命周期和范围等问题。由于捕获,Lambda 通常会稍微掩饰这些主题。
猜你喜欢
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-05
  • 2011-04-21
  • 1970-01-01
相关资源
最近更新 更多