【问题标题】:Storing a function in a class without call overhead在没有调用开销的类中存储函数
【发布时间】:2018-02-26 06:41:04
【问题描述】:

我想在类中存储一个函数或对它的引用作为成员变量。我知道存储函数指针以及 std::function 或 boosts 等价物。但是,我最近read 表示,与模板参数相比,使用指针会导致调用开销。在那次搜索中,我偶然发现了this 帖子,但我无法从答案中重现工作代码。以下是我尝试过的一些答案:

template<typename ftype>
class MyClass
{
private:
    ftype func;

public:
    MyClass(ftype func_)
    :func(func_)
    {}
};

double dosth(double x)
{
    return x*x;
}

int main()
{
    MyClass<decltype(dosth)> entity(dosth);
}

但是,它会导致错误:

错误:使用函数类型“双(双)”实例化的数据成员

注意:在此处请求的模板类 'MyClass' 的实例化中

我不确定确切的问题出在哪里,但我希望在编译时输入确切的函数类型。

所以我的问题是:编写一个以某种方式需要将重复调用(并且仅调用)的函数信息的类,我能做些什么来避免上述开销?

【问题讨论】:

  • 您问的是开销还是要编译的代码?
  • @juanchopanza 我在询问规避上述开销的方法,并且只显示了我使用该代码尝试过(但失败了)的方法。我在想这个与链接问题有关的方向可能是正确的,也许我只是犯了一个愚蠢的错误,可以很容易地解决这个问题。
  • 你有真正的性能问题,还是只是过早优化?
  • 要修复编译器错误,您可以使用ftype* func;MyClass&lt;decltype(&amp;dosth)&gt; entity(dosth);。至于关于开销的问题。我认为您需要将func 声明为内联和/或将其设为std::function
  • 您的编译器错误与开销无关。最好就一件事提出一个明确的问题。

标签: c++ templates


【解决方案1】:

其他 cmets 已经解释了如何使其编译,但请放心,这并不能解决您的性能问题。

首先,你的包装器中仍然有一个函数指针,它与std::function 的最简单形式没有太大区别。恭喜你重新发明了轮子。

模板只会改进一些事情,如果不仅仅是函数签名,而是实际函数是模板参数。只有这样,编译器才能在编译时解析函数调用并应用内联或静态评估等优化。

正是这些优化的缺乏使得函数指针变得如此昂贵。函数指针的实际调用并没有真正的影响,这就是处理器进行分支预测的原因。

【讨论】:

  • 谢谢,这听起来很有希望,但是我将如何使用该函数作为模板参数呢?直接这样做我得到“模板类型参数的模板参数必须是类型”
  • @bernharde 参见例如stackoverflow.com/questions/1174169/…(看问题中的例子,而不是答案。)
  • 非常感谢,这很有帮助。但它似乎需要我知道函数签名预编译时间?
  • @bernhard_e 是也不是。您应该可以在模板参数中只说 class functionname 而不是使用完整签名。但是编译器确实必须知道,是的。
【解决方案2】:

我认为您正在查看一个不存在的问题。来自您链接到的问题的accepted answer

+-------------------+--------------+---------------+----------------+
|                   | function ptr | std::function | template param |
+===================+==============+===============+================+
| can capture       |    no(1)     |      yes      |       yes      |
| context variables |              |               |                |
+-------------------+--------------+---------------+----------------+
| no call overhead  |     yes      |       no      |       yes      |
| (see comments)    |              |               |                |
+-------------------+--------------+---------------+----------------+
| can be inlined    |      no      |       no      |       yes      |
| (see comments)    |              |               |                |
+-------------------+--------------+---------------+----------------+
  ...

如果不能支持内联是您唯一的问题,我想说,您不必担心太多。

当推送到了紧要关头时,您可以将内联函数包装在类模板中并使用类模板。


更新,以回应 OP 的评论。

假设你想使用这个函数

double square(double in) { return in*in; }

它被包装在一个类模板中:

template <typename T> struct Square
{
    T operator() const (T in) { return in*in; }
};

现在您可以使用Square&lt;double&gt; 作为类型,而不是使用square 作为函数指针。

【讨论】:

  • 感谢您的回答,但您能否具体说明“在类模板中包装内联函数并使用类模板”是什么意思?
  • 呃,你也混淆了模板部分。这不是关于使用类型作为模板参数,而是使用实际函数。您所做的只是将模板化函数包装在不必要的结构中。
猜你喜欢
  • 2021-10-25
  • 1970-01-01
  • 1970-01-01
  • 2011-11-09
  • 2013-08-01
  • 1970-01-01
  • 2012-05-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多