Search code examples
c++templatesfunctorcrtpcallable-object

Explicit conversion of templated functors to specific functors


I have a callable struct Foo defined as

struct Foo {
    template <typename T>
    void operator()(T i) const { /* ... */ }
};

and for reasons that are out of scope I would like to statically select which type to call it with avoiding the following cumbersome notation:

Foo foo;
foo.operator()<int>(0);
foo.operator()<char>('0');
foo.operator()<char>(0);    // Notice: I want to select the implementation 
                            // so that **implicit conversions may take place**.

To this end, I'd like to implement a template member function To() such that the above can be rewritten as:

foo.To<int>()(0);
foo.To<char>()('0');
foo.To<char>()(0);

Basically, foo.To<T>() would return a callable object that can be used as a callback. One way to do this can be accomplished by using lambdas:

struct Foo {
    template <typename T>
    void operator()(T i) const { /* ... */ }

    template <typename T>
    auto To() const {
        return [this](T i) -> void {
            return this->operator()<T>(i);
        };
    }
};

However, I don't really like this solution because it creates a lambda and returns it by value, whereas I'd prefer to have some sort of "static cast" of Foo that has no computational overhead compared to a simple call to a member function. A CRTP solution can also be adopted, but it'd probably add too much boilerplate code for something that I'd expect to be much simpler to be done. What is the most efficient way to achieve the above objective?


Solution

  • Your assumption that there is extra overhead involved is not necessarily correct. Compilers are really good at optimizing things, and it's always worth confirming whether that's the case or not before spending time refactoring the code for what will amount to no benefit whatsoever.

    Case in point:

    struct Foo {
        template <typename T>
        void operator()(T i) const;
    
        template <typename T>
        auto To() const {
            return [this](T i) -> void {
                return (*this)(i);
            };
        }
    };
    
    // Directly
    void foo(const Foo& obj, char v) {
        return obj(v);
    }
    
    auto bar(const Foo& obj, int v) {
        return obj.To<char>()(v);
    }
    
    // As functors
    auto get_foo_functor(const Foo& obj) {
      return obj;
    }
    
    auto get_To_functor(const Foo& obj) {
      return obj.To<char>();
    }
    

    Gcc compiles this down to

    foo(Foo const&, char):
            movsx   esi, sil
            jmp     void Foo::operator()<char>(char) const
    bar(Foo const&, int):
            movsx   esi, sil
            jmp     void Foo::operator()<char>(char) const
    get_foo_functor(Foo const&):
            xor     eax, eax
            ret
    get_To_functor(Foo const&):
            mov     rax, rdi
            ret
    

    You can play around with the example live on godbolt here: https://gcc.godbolt.org/z/jv6ejYn39