【问题标题】:Explicit conversion of templated functors to specific functors将模板化函子显式转换为特定函子
【发布时间】:2021-08-28 11:11:15
【问题描述】:

我有一个可调用的结构 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&lt;T&gt;() 将返回一个可用作回调的可调用对象。 一种方法可以通过使用 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 解决方案,但它可能会添加太多样板代码,而我希望这样做会更简单。 实现上述目标最有效的方法是什么?

【问题讨论】:

  • 您如何确定存在计算开销?我相当有信心任何非无脑编译器都会优化它。
  • 另外,不漂亮,但foo.operator()&lt;char&gt;(0); 有效。
  • 编译器会为你做这个优化,不用费心。
  • 另外,为什么不干脆做foo.To&lt;int&gt;(0); 并完全跳过lambda?您是否打算在实际代码中调用它之前存储 lambda 并传递它?
  • @Frank @super 您是否声称 lambda 解决方案与普通函数调用一样高效? lambda解决方案至少没有额外的间接层吗?我应该存储 lambda 并通过const 引用返回它吗?

标签: c++ templates functor crtp callable-object


【解决方案1】:

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

例子:

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

【讨论】:

  • 那么,lambda 解决方案基本上没有额外的性能成本,对吧?
  • 如果是这样,那你解决了我的疑惑,非常感谢!
  • 只要您的编译器智能到一半,并且您没有忘记开启优化,那么您就可以确信不会产生任何开销。这里没有保证,但话又说回来,在性能方面从来没有。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多