简体   繁体   中英

User higher order functions in C++ 14

I'd like to create a class that accepts a function which was created by another function in Arduino. The "closest" I got after searching and a lot of trial and error (without std::function as we're in Arduino and C++ 14):

in foo.h

class Foo {
  private:
    template<typename Functor>
    Functor _lambda;

  public:
    template<typename Functor>
    Foo(Functor lambda);

    static auto create_lambda(int a) {
      return [a](int b) mutable { ... }
    }
};

in foo.cpp

template<typename Functor>
Foo::Foo (Functor lambda) : _lambda(lambda) {}

in bar.cpp

new Foo(Foo::create_lambda(2));

The code above produces a number of errors including:

error: data member '_lambda' cannot be a member template
error: 'Foo::Foo(Functor) [with Functor = Foo::create_lambda(int)::<lambda(bool)>]', declared using local type 'Foo::create_lambda(int)::<lambda(bool)>', is used but never defined

Also, happy to go with C++ 17 if that helps.

The class has to be templated, rather than the member:

// Put this whole thing in the header file. 
// It has to go there b/c templates have to be in the header.
template<class F>
class Foo {
    F func;
   public:
    Foo() = default;
    Foo(Foo const&) = default;
    Foo(Foo&&) = default;
    Foo(F f) : func(f) {}

    // Example invoke member function
    int invoke(int value) {
        return func(value);
    }
};

In C++17, we can make it figure out the type of F automatically when making a Foo by adding a template deduction guide for Foo :

// Put this after the class declaration
template <class F>
Foo(F) -> Foo<F>;

Then, we can use it pretty easily!

// Template parameter is implicit
Foo myFoo = [](int x) { return x * x; };
// myFoo now has type Foo<(anonymous lambda)>

If you absolutely need runtime polymorphism, you can have an interface that each Foo<F> extends:

class Invokable {
    virtual int invoke(int) = 0;
    virtual ~Invokable() = default;  
};

template <class F>
class Foo : Invokable {
    // ...
};

This article about the way std::function is implemented provides more details than I can in a stack overflow post: https://shaharmike.com/cpp/naive-std-function/

Addendum: You can see a working example here.

When compiled with optimization, the assembly is extremely compact. One of the benefits of C++ is that there's no overhead in using lambdas or templates, and they're entirely transparent to the compiler.

(Virtual functions are NOT transparent, which is why I do my best to avoid them, but sometimes you really need runtime polymorphism)

.LC0:
        .string "The square of %i is %i\n"
main:
        push    rbx
        xor     ebx, ebx
.L2:
        mov     edx, ebx
        mov     esi, ebx
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax
        imul    edx, ebx
        add     ebx, 1
        call    printf
        cmp     ebx, 11
        jne     .L2
        xor     eax, eax
        pop     rbx
        ret

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM