优化交换机的模板替换

Dre*_*led 12 c++ templates template-meta-programming

我的一个项目中有很多自定义数据类型,它们共享一个公共基类.

我的数据(来自数据库)有一个数据类型,它由基类的枚举区分.我的体系结构允许特定的数据类型专门用于派生类,或者它可以由基类处理.

当我构造一个我特定的数据类型时,我通常直接调用构造函数:

Special_Type_X a = Special_Type_X("34.34:fdfh-78");
a.getFoo();
Run Code Online (Sandbox Code Playgroud)

有一些模板魔术也可以像这样构建它:

Type_Helper<Base_Type::special_type_x>::Type a =  Base_Type::construct<Base_Type::special_type_x>("34.34:fdfh-78");
a.getFoo();
Run Code Online (Sandbox Code Playgroud)

对于枚举类型的某些值,可能没有专门化

Type_Helper<Base_Type::non_specialized_type_1>::Type == Base_Type
Run Code Online (Sandbox Code Playgroud)

当我从数据库中获取数据时,在编译时不知道数据类型,因此有第三种方法来构造数据类型(来自QVariant):

Base_Type a = Base_Type::construct(Base_type::whatever,"12.23@34io{3,3}");
Run Code Online (Sandbox Code Playgroud)

但是我当然希望调用正确的构造函数,所以该方法的实现过去看起来像:

switch(t) {
     case Base_Type::special_type_x:  
        return Base_Type::construct<Base_Type::special_type_x>(var);

     case Base_Type::non_specialized_type_1:  
        return Base_Type::construct<Base_Type::non_specialized_type_1>(var);              

     case Base_Type::whatever:  
        return Base_Type::construct<Base_Type::whatever>(var);     

     //.....
}
Run Code Online (Sandbox Code Playgroud)

这段代码是重复的,因为基类也可以处理新类型(添加到枚举),我提出了以下解决方案:

// Helper Template Method
template <Base_Type::type_enum bt_itr>
Base_Type construct_switch(const Base_Type::type_enum& bt, const QVariant& v)
{
  if(bt_itr==bt)
    return Base_Type::construct<bt_itr>(v);
  return construct_switch<(Base_Type::type_enum)(bt_itr+1)>(bt,v);
}

// Specialization for the last available (dummy type): num_types
template <>
Base_Type construct_switch<Base_Type::num_types>(const Base_Type::type_enum& bt, const QVariant&)
{
  qWarning() << "Type" << bt << "could not be constructed";
  return Base_Type(); // Creates an invalid Custom Type
}
Run Code Online (Sandbox Code Playgroud)

我原来的switch语句被替换为:

return construct_switch<(Base_Type::type_enum)0>(t,var);
Run Code Online (Sandbox Code Playgroud)

该解决方案按预期工作.

然而,编译的代码是不同的.虽然原始switch语句的复杂度为O(1),但新方法导致O(n)复杂度.生成的代码递归调用我的帮助器方法,直到找到正确的条目.

为什么编译器无法正确优化?有没有更好的方法来解决这个问题?

类似的问题: 在模板化和非模板化代码之间进行接口时替换switch语句

我应该提一下,我想避免使用C++ 11C++ 14并坚持使用C++ 03.

Yak*_*ont 30

这是神奇的切换问题 - 如何获取(范围)运行时值并将其转换为编译时常量.

从C++ 1y替换的样板开始:

template<unsigned...> struct indexes {typedef indexes type;};
template<unsigned max, unsigned... is> struct make_indexes: make_indexes<max-1, max-1, is...> {};
template<unsigned... is> struct make_indexes<0, is...>:indexes<is...> {};
template<unsigned max> using make_indexes_t = typename make_indexes<max>::type;
Run Code Online (Sandbox Code Playgroud)

现在我们可以轻松地创建从0到n-1的无符号整数的编译时序列. make_indexes_t<50>扩展到indexes<0,1,2,3,... ,48, 49>.C++ 1y版本在对数递归步骤中这样做,上面是线性的(在编译时 - 没有在运行时完成),但你有多少100种类型?

接下来,我们构建一个回调数组.由于我讨厌C遗留函数指针语法,我会抛出一些毫无意义的样板来隐藏它:

template<typename T> using type = T; // pointless boilerplate

template<unsigned... Is>
Base_Type construct_runtime_helper( indexes<Is...>, Base_Type::type_enum e, QVariant const& v ) {
  // array of pointers to functions:  (note static, so created once)
  static type< Base_Type(const QVariant&) >* constructor_array[] = {
    (&Base_Type::construct<Is>)...
  };
  // find the eth entry, and call it:
  return constructor_array[ unsigned(e) ](v);
}
Base_Type construct_runtime_helper( Base_Type::type_enum e, QVariant const& v ) {
  return construct_runtime_helper( make_indexes_t< Base_Type::num_types >(), e, v );
}
Run Code Online (Sandbox Code Playgroud)

和鲍勃是你的叔叔.O(1)数组查找(使用O(n)设置,理论上可以在可执行启动之前完成)

  • @dreamcooled升级你的编译器,使用chained-`if`语句,使用工具生成代码,复制pasta,使用预处理器宏来生成代码. (7认同)
  • 这是一个很好的解决方案!使用c ++ 03时有什么选择? (2认同)
  • 编译器升级了.工作完美.谢谢. (2认同)
  • 回复:“鲍勃是你的叔叔” - 这是一个 https://en.wikipedia.org/wiki/Robert_Cecil_Martin 参考吗?谢谢。 (2认同)