Explicit conversion of templated functors to specific functors

61 Views Asked by At

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?

1

There are 1 best solutions below

3
On BEST ANSWER

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