Do we have closures in C++?
Asked Answered
K

7

65

I was reading about closures on the net. I was wondering if C++ has a built-in facility for closures or if there is any way we can implement closures in C++?

Kepner answered 28/9, 2012 at 7:5 Comment(4)
Besides the answer below, also check en.cppreference.com/w/cpp/language/lambda for more of a reference page.Timms
Apparently lots of people still mix anonymous functions with closures. It is still not clear for me if C++11 indeed supports closures, or just anonymous functions ("lambdas"). I would not expect C++ to support actual closures in the same sense as in JavaScript, Python, Scala and others, would find it very surprising.Ultrafilter
Proving myself wrong, yeah, it captures variables in context, by copy and even by reference apparently, the only quirk is that there is no garbage collection... Still would like to better understand how that all works in practice en.cppreference.com/w/cpp/language/lambdaUltrafilter
scottmeyers.blogspot.nl/2013/05/lambdas-vs-closures.htmlUltrafilter
L
44

The latest C++ standard, C++11, has closures.

http://en.wikipedia.org/wiki/C%2B%2B11#Lambda_functions_and_expressions

http://www.cprogramming.com/c++11/c++11-lambda-closures.html

Logion answered 28/9, 2012 at 7:8 Comment(3)
No we don't. In C++, all closures are member functions of a hidden class that gets passed around. And because a class gets passed around, the function singnatures are incompatible. Yes, we kinda have closures, but they are NOT actual closures, they are NOT a function that has access to some other functions variables, that gets allocated and whose address can be passed around. Neither with bind, nor with lambda-expressions. And Visual Studio/VisualCPP doesn't support alloc_callback, which is used in C to do this. And you can't pass a member function to a function that expects a static function.Demonstrate
@StefanSteiger I guess Rust closures aren't actual closures either then? They're exactly like that. Do the C++11 implement some interface so they can be called dynamically, similarly to dyn Fn(x) -> y in Rust?Housefather
@StefanSteiger You can certainly cast them all to std::function<…> instances so the signatures are compatible, but with runtime overhead.Devinne
S
26

If you understand closure as a reference to a function that has an embedded, persistent, hidden and unseparable context (memory, state), then yes:

class add_offset {
private:
    int offset;
public:
    add_offset(int _offset) : offset(_offset) {}
    int operator () (int x) { return x + offset; }
}

// make a closure
add_offset my_add_3_closure(3);

// use closure
int x = 4;
int y = my_add_3_closure(x);
std::cout << y << std::endl;

The next one modifies its state:

class summer
{
private:
    int sum;
public:
    summer() : sum(0) {}
    int operator () (int x) { return sum += x; }
}

// make a closure
summer adder;
// use closure
adder(3);
adder(4);
std::cout << adder(0) << std::endl;

The inner state can not be referenced (accessed) from outside.

Depending on how you define it, a closure can contain a reference to more than one function or, two closures can share the same context, i.e. two functions can share the same persistent state.

Closure means not containing free variables - it is comparable to a class with only private attributes and only public method(s).

Strategist answered 19/10, 2013 at 9:18 Comment(12)
The behavior is similar to a closure but I would not say it is a closure.Hydromechanics
@Stepenre what would be the difference?Strategist
This hardly looks like a closure, what you have here is just an object holding a state modified by a method, plus some syntactic sugar due to the fact it is "operator()" instead of any other method. Make it return a lambda that depends on the state, or on a parameter of the method, and then we are talking.Ultrafilter
@Ultrafilter One thing is the abstract idea of a closure, another thing is how you use that idea in a programming language. In the examples above, both "my_add_3_closure" and "adder" are closures - they match the definition. The objects here is effectively a references to a function... I want to underline that the idea of closures was used in C++ programming long before C++11 and that the new lambda construct just makes it easier.Strategist
@Strategist I'd like to comment that other instances of summer will have access to each other's private sum variable, as access keywords in C++ are per-class, not per-instance. Because of this I'd argue it is not a closure (as encapsulation is a property many people expect). Lambdas on the other hand do not break this encapsulation, as each has a unique type.Rogelioroger
@AnthonyMonterrosa, you're making a mistake in your argument. private: int sum means the member can be accessed only by member functions, but it still belongs to the instance, not to the class. static int sum would belong to the class.Strategist
@Zrin. Consider the following code. Running it will show what I mean (and sorry, the formatting in a comment will be terrible I'm sure): class X { int x; public: X(int x): x(x) { } int getX(const X& instance) const { return instance.x; } void setX(X& instance, const int newX) { instance.x = newX; } }; int main() { X a(0); X b(1); // Read access std::cout << a.getX(b) << std::endl; // Write access a.setX(b, 2); std::cout << b.getX(b) << std::endl; }Rogelioroger
@Strategist as you can see, if you run that snippet in this online compiler, that a's instance methods can manipulate b's private members. public, private, and protected are compile-time guarantees and are on a per-class basis, not per-instance.Rogelioroger
@AnthonyMonterrosa, your critique of the examples I've given in the answer above is that the closure's context is not hidden well enough, because there is nothing that would stop an ignorant programmer to deliberately and obviously break the concept. But if the creator doesn't want a closure, then why bother anyway?Strategist
@Strategist I was just pointing out a technical detail. Functionally it's good enough.Rogelioroger
For anyone who thinks this isn't a closure. Functionally, this is definitely a closure. It might not look like a closure compared to one's personal experience. But the visual looks of what one considers a closure is entirely a personal opinion and isn't really definable. At the end of the day, all that really matters if the functionality.Purism
Note that this is exactly how C++11's lambda is implemented internally, except that the names in compiler-generated lambdas are not actually accessible.Devinne
H
22

Yes, This shows how you could implement a function with a state without using a functor.

#include <iostream>
#include <functional>
 
std::function<int()> make_my_closure(int x) {
    return [x]() mutable {   
        ++x;
        return x;   
    };
}

int main()  {
    auto my_f = make_my_closure(10);
    
    std::cout << my_f() << std::endl; // 11
    std::cout << my_f() << std::endl; // 12
    std::cout << my_f() << std::endl; // 13
    
     auto my_f1 = make_my_closure(1);

    std::cout << my_f1() << std::endl; // 2
    std::cout << my_f1() << std::endl; // 3
    std::cout << my_f1() << std::endl; // 4

    std::cout << my_f() << std::endl; // 14
}
Hydromechanics answered 11/11, 2015 at 14:39 Comment(2)
To be really sure, it would be nice to see what happens if you run my_f() again after my_f1(), because it might still be the same variable that you have just directly assigned with the call to make_my_closure.Ultrafilter
x is a temporary value in make_my_closure. So IMO it would not make sense. I added the case you asked. Which behave as expectedHydromechanics
V
9

I suspect that it depends on what you mean by closure. The meaning I've always used implies garbage collection of some sort (although I think it could be implemented using reference counting); unlike lambdas in other languages, which capture references and keep the referenced object alive, C++ lambdas either capture a value, or the object refered to is not kept alive (and the reference can easily dangle).

Velamen answered 28/9, 2012 at 7:29 Comment(0)
L
7

Yes, C++11 has closures named lambdas.

In C++03 there is no built-in support for lambdas, but there is Boost.Lambda implementation.

Launch answered 28/9, 2012 at 7:10 Comment(0)
F
0

Strictly speaking. 'Closure' is LISP only. Use Let returns lambda as last commands. 'Let Over Lambda'. This is possible only for LISP because of infinite scope with lexical scoping. I don't know any other language support this natively until know.

(defun my-closure ()
   (let ((cnt 0))
      (lambda () 
         (format t "called : ~A times" (incf cnt)))))
Finsteraarhorn answered 12/4, 2020 at 16:2 Comment(0)
E
0

You can achive similar functionality using static variables and lambdas.

#include <iostream>
#include<functional>

int main()
{
    std::function<std::function<int()>()> generator_function=[]()->std::function<int()>{
        static int i=0;
        return [&]()->int{
            return i++;
        };
    };
    
    std::function<int()> iterator_function=generator_function();
    
    std::cout<<iterator_function()<<std::endl;  //1
    std::cout<<iterator_function()<<std::endl;  //2
    std::cout<<iterator_function()<<std::endl;  //3 
    std::cout<<iterator_function()<<std::endl;  //4
    return 0;
}
Everywhere answered 12/5, 2022 at 13:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.