开发者

Closures in C++

开发者 https://www.devze.com 2022-12-16 00:39 出处:网络
I\'ve found myself in a strange place, mentally. In a C++ project, I long for closures. Background. There\'s a Document-type class with a public Render method which spawns a deep call tree. There\'s

I've found myself in a strange place, mentally. In a C++ project, I long for closures.

Background. There's a Document-type class with a public Render method which spawns a deep call tree. There's some transient state that only makes sense during rendering. Right now it resides in the class like regular member variables. However, this is not satisfactory on some levels - this data only makes sense during a Render开发者_StackOverflow call, why store it all the time? Passing it around in arguments would be ugly - there are around 15 variables there. Passing around a structure would add a lot of "RenderState->..." in the lower-level methods.

So what do I want? I want the world, like we all do. Specifically, a set of variables that are:

  • available to some methods in a class (not all of them)
  • accessible by name alone (no pState->... stuff - so that refactoring is easy)
  • not copied around on every method call
  • only live during a method call and up its call tree (assuming trees grow up)
  • live on a stack

I know I can have some of those properties with C++ - but not all of them. Tell me I'm not turning weird.

Heck, in Pascal, of all places, nested functions give you all that...

So what is a good workaround to emulate closures in C++, getting as many of the above benefits as possible?


Standard C++ since C++11 provides native lambda expressions and several compilers (VC10+ GCC and clang at least) implements it.

With GCC and Clang you can activate it with "--std=c++11" (or use a higher version of C++ if available). VC10 and later versions have it activated without need for flags.

By the way, you can also use boost::lambda (that is not perfect but works with C++03) also provide lambda in C++.


You don't have nested functions, but you have local classes:

void Document::Render(Param)
{
    class RenderState
    {
    public:
        RenderState(Document&)
        {
           //...
        }

        void Go(Param);

    private:
        // "Nested" functions
        //  ....

        // Data that nested functions operate on
        // ...
    };

    RenderState s(*this);
    s.Go(Param);
}

See this GotW article for more information


Personally, I'd go with the RenderState approach.

Alternatively, if there's a well-defined set of Render-only functions that all require access to the same data, I'd seriously investigate pulling those into their own DocumentRenderer class that contains both the appropriate methods and the appropriate member variables. (This is similar to Fowler's "method object" refactoring.)

C++ doesn't have nested functions, but local classes can serve as an imperfect solution. (Imperfect because local classes' methods cannot access variables of the enclosing class and because they can't be used to instantiate templates.) A local class is simply a class that's declared, along with its methods, within the body of a function. Herb Sutter discusses local classes in more detail here.

Local classes are used to implement Boost's ScopeExit library. ScopeExit's reviewers noted that ScopeExit "suggests a method for creating a general closure mechanism as a library," so if you aren't happy with a RenderState or DocumentRenderer approach, ScopeExit's implementation may give you some ideas for closures in C++.


Currently there are no closures in C++ that would generate "orinary" first-class functions (whether member or non-member). Moreover, there's no standard way to implement such closures.

Closure semantics is available for functors in template metaprogramming at compile time, but that's a completely different kind of beast. In order to obtain a true run-time closure functionality for first-class functions you haver no other choice but to use a non-standard low-level implementation like this one, for example.


A functor is basically a closure.


Why the downvotes? Take Érics comment, change void Go(Param); to void operator () (Param); and there you have it.

There is no way to keep the stack in a native application after the function has exited. But this would be neccesary to make closures like the ones in Javascript. And there is no way to reference a function's stack without doing anything evil. A class that acts like a function (=a functor) would have to get all the relevant information passed somehow, but this is as close as you get in C++. It has state, it has code, and you can pass it around.

Please explain, where am I wrong?


As long as the local variables you want to bind are in scope, you can try something like the following to bind them to your inner class. Though, if you have read the above posted GotW article, it is a fragile solution.

#include <iostream>
using namespace std;

int main() {
  int x = 1;
  cout << x << endl; // 1

  class Inner {
  public:
    Inner(int& x) : bound_x(x) {}
    void do_sth() { ++bound_x; }
  private:
    int& bound_x;
  };

  Inner i(x);
  i.do_sth();
  cout << x << endl; // 2
  x = 5;
  i.do_sth();
  cout << x << endl; // 6
  return 0;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号