C++ 运行时期间的非多态容器

C++ 运行时期间的非多态容器,c++,templates,c++11,template-meta-programming,C++,Templates,C++11,Template Meta Programming,我正在尝试将对象存储在几种容器类型中的一种,容器的类型在运行时是已知的。 容器的接口是相同的,但容器不是多态的 我试图做的是避免在运行时vtable强制的额外跳转-通过使用一个应该是具体容器类型的变量 考虑到我有以下容器: class Vector1<T> { T get(); } class Vector2<T> { T get(); } 类向量1 { T get(); } 类向量2 { T get(); } 我想要一个容器,它要么是Vector

我正在尝试将对象存储在几种容器类型中的一种,容器的类型在运行时是已知的。 容器的接口是相同的,但容器不是多态的

我试图做的是避免在运行时vtable强制的额外跳转-通过使用一个应该是具体容器类型的变量

考虑到我有以下容器:

class Vector1<T>
{
     T get();
}
class Vector2<T>
{
     T get();
}
类向量1
{
T get();
}
类向量2
{
T get();
}
我想要一个容器,它要么是Vector1,要么是Vector2,这取决于一些运行时参数

所以我的课程应该是:

Class VectorWrapper
{
      Vector1<SomeType> v1; 
      Vector2<Sometype> v2;

      inline Sometype get()
      {
          **how do I choose between v1 and v2, without the extra jump?**
      }
}
类向量包装器
{
向量1 v1;
矢量2v2;
内联Sometype get()
{
**在没有额外跳转的情况下,如何在v1和v2之间进行选择**
}
}

将程序中使用这些容器的部分参数化为容器类型:

template<class Container>
void criticalPath()
{
    // Create and use Container objects a lot
}


void enterCriticalPath(bool useVector1) {
    if (useVector1) {
        criticalPath<Vector1<SomeType>>();
    } else {
        criticalPath<Vector2<SomeType>>();
    }
}
模板
无效关键路径()
{
//大量创建和使用容器对象
}
无效enterCriticalPath(bool useVector1){
如果(使用矢量1){
临界路径();
}否则{
临界路径();
}
}

将程序中使用这些容器的部分参数化为容器类型:

template<class Container>
void criticalPath()
{
    // Create and use Container objects a lot
}


void enterCriticalPath(bool useVector1) {
    if (useVector1) {
        criticalPath<Vector1<SomeType>>();
    } else {
        criticalPath<Vector2<SomeType>>();
    }
}
模板
无效关键路径()
{
//大量创建和使用容器对象
}
无效enterCriticalPath(bool useVector1){
如果(使用矢量1){
临界路径();
}否则{
临界路径();
}
}

为了避免在运行时做出决定,您必须在编译时做出决定。这意味着使用模板。如果你的时间非常有限,在关键路径上真的不能有非必要的跳跃,那么你必须在你的程序中不断地提高温度,直到你达到一个可以进行额外跳跃的点。是的,从理论上讲,这可能意味着模板中包含>=90%的代码,但如果您确实需要避免跳转,这就是代价

换句话说,如果
VectorWrapper
不能用
if
来决定,那么它也必须是一个模板。使用它的代码也必须如此,等等,直到你最终达到一个点,
if
不是那么昂贵


如果您发现这个模板化的部分太大,但至少可以以某种方式隔离,您甚至可以做一些事情,比如构建几个共享库(每个库中都有不同的模板参数),并在运行时加载一个包含正确模板参数组合的版本。

为了避免在运行时做出决定,您必须在编译时做出决定。这意味着使用模板。如果你的时间非常有限,在关键路径上真的不能有非必要的跳跃,那么你必须在你的程序中不断地提高温度,直到你达到一个可以进行额外跳跃的点。是的,从理论上讲,这可能意味着模板中包含>=90%的代码,但如果您确实需要避免跳转,这就是代价

换句话说,如果
VectorWrapper
不能用
if
来决定,那么它也必须是一个模板。使用它的代码也必须如此,等等,直到你最终达到一个点,
if
不是那么昂贵


如果您发现这个模板化的部分太大,但至少可以以某种方式隔离,您甚至可以做一些事情,比如构建几个共享库(每个库中都有不同的模板参数),并在运行时加载一个包含正确模板参数组合的版本。

您的问题很模糊。所以我只能提供模糊的解决方案

第一种值得考虑的技术是手动vtable。与自动vtable相比,这将删除内存间接寻址。基本上,您存储了一个指向方法正确实现的函数指针,并直接遵循该函数指针

第二种方法是将分支从回路中提升出来。重要的是,您的代码必须重复运行。如果在重复之前决定采用哪个分支,则可以将分支移出

想象一下你有

void foo() {
  bool which_branch = decide();
  VectorWrapper wrap( decide );
  wrap.populate();
  for (auto x : some_loop_domain) {
    x.set( wrap.get() );
  }
}
请注意,决策是在循环之外做出的,但价格可能是在循环内支付的

如果我们可以将分支移到循环之外,那么成本将支付一次,而不是在
some\u loop\u domain
中的每个元素支付一次

一种方法是为编译器提供一个更容易优化的常量。因此,我们避免在包装器中存储向量正在使用的状态,而是将其传入。我们传递的值在使用它的上下文中被安排为“显然是常量”,甚至是编译时常量

要使用编译时常量,必须将使用它的代码模板化。然而,这种模板化只需要在“循环之上”点进行,因为那里的分支通常足够便宜,不必在意

为了相信优化器是合理的,您同样会在那里填充一个明显的常量,并将其作为值传递。根据我的经验,这可能不太可靠

void foo() {
  bool which_branch = decide();

  auto body = [&](auto which_branch) {
    VectorWrapper wrap( which_branch );
    wrap.populate();
    for (auto x : some_loop_domain) {
      x.set( wrap.get() );
    }
  };
  if (which_branch) {
    body( std::true_type{} );
  } else {
    body( std::false_type{} );
  }
}
现在在
body
中,
哪个分支是编译时常量。这本身可能导致树枝被吊起,但可能需要:

  auto body = [&](auto which_branch) {
    static_assert( decltype(which_branch){} || !decltype(which_branch){} );
    VectorWrapper<decltype(which_branch)> wrap;
    wrap.populate();
    for (auto x : some_loop_domain) {
      x.set( wrap.get() );
    }
  };
autobody=[&](自动哪个分支){
静态断言(decltype(which|branch){}decltype(which|branch){});
矢量包装;
wrap.populate();
for(自动x:某些循环域){
x、 set(wrap.get());
}
};
我们在类上创建
VectorWrapper
一个
template
,当在
bool
上下文中实例化和读取时,该类返回
true
false
作为
constepr

VectorWrapper
中的代码保持相似,只是它读取编译时常量,而不是决定采用哪个分支的方法

这项技术可能非常具有侵入性