Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ save lambda-functions as member-variables without function pointers for optimization

I would like to write in C++ a class that saves lambda functions as member variables. It would be great to do it as efficient as possible. For example, I read this thread Why can lambdas be better optimized by the compiler than plain functions? and therefore I want to avoid using function pointers.

So far my best solution is the following:

template<typename F>
class LambdaClass {
  private:
    F lambdaFunc;
  public:
    LambdaClass(F &_lambdaFunc): lambdaFunc(_lambdaFunc) {}
};

I would use this class as follows:

auto lambdaFunc = [](int _a) -> int { return _a; };
LambdaClass<decltype(lambdaFunc)> lambdaClassObject<decltype(lambdaFunc)>(lambdaFunc);

In my opinion this doesn't look like fun using it. So I am interested in first if this code is efficient in the sense that the compiler could inline the calls of the saved member lambda function and second how one could write this code more beautiful?

Edit: I am using C++ 11.

like image 798
cowculus Avatar asked Aug 14 '18 09:08

cowculus


1 Answers

In your example

LambdaClass<decltype(lambdaFunc)> lambdaClassObject<decltype(lambdaFunc)>(lambdaFunc);

the second template argument list is incorrect syntax. This needs to be just

LambdaClass<decltype(lambdaFunc)> lambdaClassObject(lambdaFunc);

So I am interested in first if this code is efficient in the sense that the compiler could inline the calls of the saved member lambda function

Yes, this class can be used in ways that will allow optimizations pretty much just like using a lambda directly. The template argument is the exact type of the lambda expression, and template substitution happens at compile time, usually giving results just like you would get by writing out code without using templates.

How one could write this code more beautiful?

@lubgr's answer already mentions the C++17 "class template deduction" and "deduction guide" features. Prior to C++17, the usual trick to avoid needing to specify class template arguments is a helper "make function":

template <typename F>
auto makeLambdaClass(F&& func) ->
    LambdaClass<typename std::decay<F>::type>
{ return { std::forward<F>(func); } }

Now you can do

auto lambdaFunc = [](int _a) -> int { return _a; };
auto lambdaClassObject = makeLambdaClass(lambdaFunc);

But to go a step further and make

auto lambdaClassObject = makeLambdaClass( [](int _a) -> int { return _a; } );

also work, you'll also need to make sure the class has a constructor that accepts an rvalue, not just a non-const lvalue:

template<typename F>
class LambdaClass {
  private:
    F lambdaFunc;
  public:
    LambdaClass(const F &lambdaFunc_): lambdaFunc(lambdaFunc_) {}
    LambdaClass(F &&lambdaFunc_) : lambdaFunc(std::move(lambdaFunc_)) {}
};

By the way, this class will work just as well with a callable class that is not a lambda's closure type, since a lambda is just a more convenient way of defining a class with an operator():

class UniqueUIntGenerator
{
public:
    unsigned int operator()() const noexcept
    { return num++; }
private:
    static unsigned int num;
};
unsigned int UniqueIntGenerator::num = 0;

LambdaClass<UniqueIntGenerator> gen{UniqueIntGenerator{}};
like image 73
aschepler Avatar answered Oct 13 '22 21:10

aschepler