繁体   English   中英

将C ++模板对象存储为相同类型

[英]Storing C++ templated objects as same type

我有一个类,它是对性能敏感的代码路径的核心组件,因此我正在尝试尽可能地对其进行优化。 该类曾经是:

class Widget
{
    Widget(int n) : N(n) {}
    .... member functions that use the constant value N ....
    const int N;                // just initialized, will never change
}

构造函数的参数在编译时是已知的,因此我将此类更改为模板,以便可以将N编译为函数:

template<int N>
class Widget
{
   .... member functions that use N ....
}

我有另一种方法的类:

Widget & GetWidget(int index);

但是,在对窗口小部件进行模板化之后,每个窗口小部件都具有不同的类型,因此我无法再定义这种功能。 我考虑了不同的继承选项,但是我不确定模板的性能提升是否会超过继承函数调用的成本。

所以,我的问题是这样的:

我很确定我想要两全其美(编译时/运行时),这可能是不可能的。 但是,有没有一种方法可以提高在编译时知道N的性能,但仍然能够返回相同类型的Widget?

谢谢!

这里的问题是,如果您将小部件存储为相同类型,那么在编译时从该存储中检索小部件(通过调用GetWidget)的代码将知道N [*]。 调用构造函数的代码知道N,但是使用该对象的代码必须应对多种可能性。

由于对性能的影响(如果有的话)很可能是在使用的窗口小部件,而不是创建它们的代码的代码,你不能避免做以取决于运行时信息的关键代码的东西

可能是对类模板中实现的函数的虚拟调用比不使用值而不使用N的函数的非虚拟调用要快:

class Widget {
  public:
    virtual ~Widget() {}
    virtual void function() = 0;
};

template <int N>
class WidgetImpl : public Widget {
  public:
    virtual void function() { use N; }
};

当已知N时,优化器可能会尽力而为,因为它可以最佳地展开循环,变换算法等。 但是从虚拟呼叫开始,您将看到一个很大的缺点,那就是没有一个呼叫可以内联(而且我猜想虚拟呼叫比非虚拟呼叫更容易被预测,而不是在内联时) )。 内联未知N的收益可能大于了解N的收益,也可能更少。 都尝试一下,看看。

为了进行更艰巨的工作,如果出现的通用案例数量较少,您甚至可以通过将关键的窗口小部件功能实现为以下方式来看到改进:

switch(n) {
    case 1: /* do something using 1 */; break;
    case 2: /* do the same thing using 2 */; break;
    default: /* do the same thing using n */; break;
};

在所有情况下都可以“执行某些操作”,但是默认值可以是对以常量为模板的函数的调用,然后默认值为具有函数参数而不是模板参数的相同代码。 也可以全部调用同一函数(带有函数参数),但是在参数恒定的情况下,依赖编译器在优化之前内联该调用,以得到与模板化相同的结果。

无法大规模维护,通常这样猜测优化器通常是个坏主意,但也许您知道什么是常见情况,而编译器则没有。

[*]如果调用代码在编译时确实知道N的值,则可以将GetWidget替换为以下函数模板:

template <int N>
Widget<N> &getWidget(int index) {
    return static_cast<Widget<N> &>(whatever you have already);
}

但是我认为呼叫者不知道,因为如果知道,那么您可能不会问...

您需要声明一个继承自模板类型的非模板类型,然后将小部件存储为指向非模板基类的指针。 那是完成您正在寻找的唯一(类型安全)方法。

但是,保留非模板版本可能更清洁。 您是否分析了代码以查看运行时配置版本上的循环实际上是瓶颈?

我想以下不是一种选择吗?

template <int N>
Widget<N> & GetWidget();

无论如何,一旦您同时管理多种窗口小部件类型,就无法再将它们作为模板,因为您无法将不同类型的对象存储在一个容器中。

Michael提出的非模板基类是一个解决方案,但是由于它将产生虚拟函数调用成本,所以我猜想使该类模板化并没有任何好处。

如果类型是有限的并且是已知的 ,则可以使用boost::variant作为构造函数的参数。

变体类模板是一个安全的,通用的,基于堆栈的可区分联合容器,它提供了一种简单的解决方案,用于以统一的方式处理异构类型中的对象。 诸如std :: vector之类的标准容器可能被认为是“多值,单类型”,而变体是“多类型,单值”。

这是一些伪代码

boost::variant< int, double, std::string > variant;
const variant foo( 1 );
const variant bar( 3.14 );
const variant baz( "hello world" );

const Widget foo_widget( foo );
const Widget bar_widget( bar );
const Widget baz_widget( baz );

另外,您可以使用boost::any获得更大的灵活性

您可以编写模板化的GetWidget函数。 这将要求您在调用GetWidget时知道类型:

w = GetWidget<Box>(index);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM