首页 > 解决方案 > 将模板化函子显式转换为特定函子

问题描述

我有一个可调用的结构Foo定义为

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

并且由于超出范围的原因,我想静态选择要调用它的类型,避免使用以下繁琐的符号:

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(),以便上面可以重写为:

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

基本上,foo.To<T>()将返回一个可用作回调的可调用对象。一种方法可以通过使用 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);
        };
    }
};

但是,我不太喜欢这种解决方案,因为它创建了一个 lambda 并按值返回它,而与对成员函数的简单调用相比,我更喜欢某种没有计算开销的“静态Foo转换” . 也可以采用 CRTP 解决方案,但它可能会添加太多样板代码,而我希望这样做会更简单。实现上述目标的最有效方法是什么?

标签: c++templatesfunctorcrtpcallable-object

解决方案


您认为涉及额外开销的假设不一定正确。编译器真的很擅长优化事情,在花时间重构代码之前总是值得确认是否是这种情况,因为这不会带来任何好处。

一个例子:

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 将其编译为

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

您可以在这里使用 Godbolt 上的示例:https ://gcc.godbolt.org/z/jv6ejYn39


推荐阅读