gpt4 book ai didi

c++ - Lambda 自身返回 : is this legal?

转载 作者:IT老高 更新时间:2023-10-28 11:34:26 26 4
gpt4 key购买 nike

考虑一下这个相当无用的程序:

#include <iostream>
int main(int argc, char* argv[]) {

int a = 5;

auto it = [&](auto self) {
return [&](auto b) {
std::cout << (a + b) << std::endl;
return self(self);
};
};
it(it)(4)(6)(42)(77)(999);
}

基本上,我们正在尝试创建一个返回自身的 lambda。

  • MSVC 编译程序并运行
  • gcc 编译程序,但出现段错误
  • clang 用一条消息拒绝该程序:

    error: function 'operator()<(lambda at lam.cpp:6:13)>' with deduced return type cannot be used before it is defined

哪个编译器是正确的?是否存在静态约束违规、UB 或两者都没有?

更新这个轻微的修改被clang接受:

  auto it = [&](auto& self, auto b) {
std::cout << (a + b) << std::endl;
return [&](auto p) { return self(self,p); };
};
it(it,4)(6)(42)(77)(999);

更新 2:我了解如何编写一个返回自身的仿函数,或者如何使用 Y 组合子来实现这一点。这更像是一个语言律师问题。

更新 3:问题是不是 lambda 通常返回自身是否合法,而是关于这种特定方式的合法性。

相关问题:C++ lambda returning itself .

最佳答案

根据 [dcl.spec.auto]/9,程序格式错误(clang 是正确的) :

If the name of an entity with an undeduced placeholder type appears in an expression, the program is ill-formed. Once a non-discarded return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements.

基本上,内部 lambda 的返回类型的推导取决于它本身(这里命名的实体是调用运算符) - 所以你必须显式地提供一个返回类型。在这种特殊情况下,这是不可能的,因为您需要内部 lambda 的类型但无法命名。但是在其他情况下,尝试强制使用这样的递归 lambda 也可以。

即使没有这个,你也有一个 dangling reference .


在与更聪明的人(即 T.C.)讨论之后,让我再详细说明一下原始代码(略微减少)和提议的新版本(同样减少)之间有一个重要区别:

auto f1 = [&](auto& self) {
return [&](auto) { return self(self); } /* #1 */ ; /* #2 */
};
f1(f1)(0);

auto f2 = [&](auto& self, auto) {
return [&](auto p) { return self(self,p); };
};
f2(f2, 0);

也就是内部表达式self(self)不依赖于f1,但是self(self, p)是依赖的对于 f2。当表达式不依赖时,它们可以被使用......急切地使用( [temp.res]/8 ,例如 static_assert(false) 是一个硬错误,无论它发现自己的模板是否被实例化)。

对于f1,编译器(比如clang)可以尝试急切地实例化它。一旦你在上面的 #2 点到达 ;,你就知道外部 lambda 的推断类型(它是内部 lambda 的类型),但我们正在尝试使用它比这更早(把它想象成 #1 点)——在我们知道它实际上是什么类型之前,我们仍在解析内部 lambda 时尝试使用它。这与 dcl.spec.auto/9 相冲突。

但是,对于 f2,我们不能尝试急切地实例化,因为它是依赖的。我们只能在使用点进行实例化,到那时我们就知道了一切。


为了真正做这样的事情,你需要一个 y-combinator .论文中的实现:

template<class Fun>
class y_combinator_result {
Fun fun_;
public:
template<class T>
explicit y_combinator_result(T &&fun): fun_(std::forward<T>(fun)) {}

template<class ...Args>
decltype(auto) operator()(Args &&...args) {
return fun_(std::ref(*this), std::forward<Args>(args)...);
}
};

template<class Fun>
decltype(auto) y_combinator(Fun &&fun) {
return y_combinator_result<std::decay_t<Fun>>(std::forward<Fun>(fun));
}

而你想要的是:

auto it = y_combinator([&](auto self, auto b){
std::cout << (a + b) << std::endl;
return self;
});

关于c++ - Lambda 自身返回 : is this legal?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52192389/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com