Can I create anonymous classes in C++ and capture the outside variables like in Java?

woodings picture woodings · Jan 16, 2013 · Viewed 35.6k times · Source

In Java, when I need a callback function, I have to implement an anonymous class. Inside the anonymous class, I can access the outside variables if they're final.

Now I'm doing the same thing in C++. I understand that C++ lambda works better but sometimes I need to pass in many functions where with anonymous classes, I only need to pass in one instance.

I tried the following example. It works with GCC 4.3.4.

class IA {
public:
  virtual int f(int x) = 0;  
};

int main() {
    class : public IA {
        int f(int x) { return x + 1; }
    } a;
    doFancyWork(&a);
    return 0;
}

Is it possible to capture the outside variables like this?

int main() {
    int y = 100; // mark y as final if possible
    class : public IA {
        int f(int x) { return x + y; }
    } a;
    return 0;
}

UPDATE:

The second example won't compile. The errors are here,

prog.cpp: In member function ‘virtual int main()::<anonymous class>::f(int)’:
prog.cpp:9: error: use of ‘auto’ variable from containing function
prog.cpp:7: error:   ‘int y’ declared here
prog.cpp: In function ‘int main()’:
prog.cpp:7: warning: unused variable ‘y’

UPDATE:

I just realized a few more problems in doing this:

  • I cannot write a constructor because the class doesn't have a name
  • initializer list doesn't allow inheritance.
  • any change to make it compile makes the code unreadable.

I think I have to move away from anonymous classes.

Answer

Andy Prowl picture Andy Prowl · Jan 16, 2013

There is no way to automatically capture those variables, but you can use an alternative approach. This is if you want to capture by reference:

int main() {
    int y = 100; // mark y as final if possible
    class IB : public IA {
    public:
      IB(int& y) : _y(y) {}
      int f(int x) { return x + _y; }
    private:
      int& _y;
    } a (y);
    return 0;
}

If you want to capture by value, just change int& into int.

Anyway, you may consider using a tuple of lambdas as a "multi-callback" object if that is what bothers you about individual lambdas. You would still have everything packed in one object and capturing would be done for free.

Just as an example:

auto callbacks = make_tuple(
    [] (int x) { cout << x << endl; },
    [&] () { cout << y << endl; }, // y is captured by reference
    [=] (int x) { cout << x + y << endl; }, // y is captured by value
    // other lambdas here, if you want...
    );