C++ 在闭包中,如何通过存储在内存中的指针或引用类型捕获可变数据,或者如何用现代函数语言处理可变数据?

C++ 在闭包中,如何通过存储在内存中的指针或引用类型捕获可变数据,或者如何用现代函数语言处理可变数据?,c++,objective-c,swift,compiler-construction,closures,C++,Objective C,Swift,Compiler Construction,Closures,我正在写一本用于教育目的的书信。 我的transpiler从我的语言转换为C语言。 我现在正在编写闭包语法分析器和代码生成组件 我看到人们说C++中的闭包实际上被转换成未命名的结构类型,其中捕获的值作为变量。 此代码 int c = 10; auto closure = [=] () -> void { std::cout << c << std::endl; }; int c被捕获,但一旦a函数堆栈被销毁,该int c也将被销毁。这意味着,如果我们试图

我正在写一本用于教育目的的书信。 我的transpiler从我的语言转换为C语言。
我现在正在编写闭包语法分析器和代码生成组件

我看到人们说C++中的闭包实际上被转换成未命名的结构类型,其中捕获的值作为变量。

此代码

int c = 10;
auto closure = [=] () -> void {
    std::cout << c << std::endl;
};
int c
被捕获,但一旦
a函数
堆栈被销毁,该
int c
也将被销毁。这意味着,如果我们试图在解除分配的地址上写入,我们可能会运行
分段错误(内核转储)
指针错误

Java

// suppose this callback interface exists
public interface VoidCallback {
    public void method();
} 


public void aMethod() {
    int c = 10;
    VoidCallback callback = () -> c = 10; /* this gives an error */
    // local variables referenced from a lambda expression must be final or effectively final
}
__block int c = 0;
// they say, this `int c` is allocated in heap instead of stack
// so that it exists until the closure is finished executing.
void (^ closure) (void) = ^void() {
    c = 10; // this is valid
    // this actually changed the `int c`'s value
}; 
Java像这样处理闭包,并确保闭包捕获(比如隐式捕获)不会发生变化。这意味着Java传递闭包捕获副本而不是引用。对于引用或类类型,只有对象指针作为副本传递。尽管指针引用不会发生变化,但您可以在指针指向的对象内变化内容。这与前一个基本相同

在Objective-C中

// suppose this callback interface exists
public interface VoidCallback {
    public void method();
} 


public void aMethod() {
    int c = 10;
    VoidCallback callback = () -> c = 10; /* this gives an error */
    // local variables referenced from a lambda expression must be final or effectively final
}
__block int c = 0;
// they say, this `int c` is allocated in heap instead of stack
// so that it exists until the closure is finished executing.
void (^ closure) (void) = ^void() {
    c = 10; // this is valid
    // this actually changed the `int c`'s value
}; 
Swift中

var a : Int = 10;
var closure = { [] () -> Void in
    a = 10; // this is valid by default
    // unless `var a` is declared as `let a`
};
因此,这意味着Objective-C和Swift将原始捕获列表分配为指针。这样它们就可以变异

注:请注意,Swift闭包捕获列表仅适用于类或引用类型,但这里我指的是对基本类型的隐式捕获

这是

__block int c = 0;
// they say, this `int c` is allocated in heap instead of stack
// so that it exists until the closure is finished executing.
void (^ closure) (void) = ^void() {
    c = 10; // this is valid
    // this actually changed the `int c`'s value
}; 
与此(基本上)相同

我认为,在闭包完成后立即释放指针变量太糟糕了。
如果有很多闭包指向变量,并且在执行时会发生变化,该怎么办?
如果这些闭包是在不同的线程之间传递或执行的呢

我提出了一个使用引用计数技术的解决方案。
当创建一个改变变量的闭包时,该变量将被保留。
当变异变量的闭包被破坏时,变量将被释放。
当没有闭包时,变量将真正被释放。 为了确保线程安全,我将在闭包操作引用计数技术时锁定和解锁计数器变量地址

如果还有其他技巧,请指导我。
任何语言的解释都将不胜感激

目前,我对汇编语言一无所知

对于主持人, 因为这个问题是一种研究,我请求你不要太宽泛。

以下内容让我震惊:“我写的是一个用于教育目的的Transpiler。我的Transpiler可以从我的语言传输到C语言。”现在,这意味着你的语言规范定义了它应该如何操作!我们无法告诉你你的语言应该如何运作

现在,您已经找到了一系列选项:

  • C++没有对局部变量做任何特殊的处理。如果您保留对它的引用,并在它超出范围时使用它,则会带来厄运。这是C++精神,不给你带来任何负担,但如果你不注意,你可以让自己在脚上开枪。<
  • Java只是检查代码,并告诉您是否尝试执行它认为不一定有效的任何操作,否则会给您一个错误。它不允许你射中自己的脚,即使你非常想要它
  • 其他语言似乎将作用域有限的局部变量转换为基于堆的局部变量。我不确定他们的对象模型,但是在Python中,你根本没有任何类似C++局部变量或java原始类型的东西,就像你没有确定性析构函数调用一样(你用<代码>和<代码>获得相似的东西,仅仅为了完整性),所以这没有任何区别。这些语言给您带来了额外的开销,以保证您没有任何悬而未决的引用(即使您真的不需要它)

现在,第一件事是决定哪一个最适合您的语言的对象模型。只有到那时,问题才会出现,如何最好地实施它。关于实现,有许多不同的方法。使用引用计数器是一个(用无锁实现,原子操作),使用链表是另一个,或者使用垃圾回收器。< /p>这里没有任何C代码,您可能是指C++?如果是,请调整标签。@ FelixPalman标签C,因为我正在转机到C。现在我将标签改为C++,因为这会使歧义。提前谢谢迪莫:这太宽泛了。也许是一个更好的提问论坛,但一定要先查看他们的常见问题。@MartinR谢谢你的建议,先生:在过去,那些被称为“编译器”。我猜后来有个孩子认为“编译”是翻译到比C更低的级别,于是想出了令人讨厌的“transpiling”。谢谢你的建议。我能感觉到引用计数和垃圾收集器。但是我曾经考虑过在这种情况下链表的实现。我目前避免使用垃圾收集技术,因为它是在有限的系统资源上通过引用执行的。我必须阅读关于这种事情的链表。
int * c = malloc(sizeof(int));
*c = 0;
void (^ closure) (void) = ^void() {
    *c = 10;
    if (c) {
        free(c);
        c = NULL;
    }
};