C 11 方法在运行时索引元组而不使用 switch

新手上路,请多包涵

我有一段类似如下的 c++11 代码:

 switch(var) {
   case 1: dosomething(std::get<1>(tuple));
   case 2: dosomething(std::get<2>(tuple));
   ...
}

有什么办法可以去掉这个大开关吗?请注意, get<var> 不起作用,因为 var 不是恒定的,但我知道 var 的范围很小,即(0-20)。

请注意,这里的重点是避免使用导致数组查找的数组…

编辑:

关于性能问题,有一个关于 if 和 switch 语句的函数数组性能的 讨论

为了我自己的目的,我不争论哪个更好。

原文由 w00d 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 331
2 个回答

这是一个不使用索引序列的版本:

 template <size_t I>
struct visit_impl
{
    template <typename T, typename F>
    static void visit(T& tup, size_t idx, F fun)
    {
        if (idx == I - 1) fun(std::get<I - 1>(tup));
        else visit_impl<I - 1>::visit(tup, idx, fun);
    }
};

template <>
struct visit_impl<0>
{
    template <typename T, typename F>
    static void visit(T& tup, size_t idx, F fun) { assert(false); }
};

template <typename F, typename... Ts>
void visit_at(std::tuple<Ts...> const& tup, size_t idx, F fun)
{
    visit_impl<sizeof...(Ts)>::visit(tup, idx, fun);
}

template <typename F, typename... Ts>
void visit_at(std::tuple<Ts...>& tup, size_t idx, F fun)
{
    visit_impl<sizeof...(Ts)>::visit(tup, idx, fun);
}

演示

原文由 Oktalist 发布,翻译遵循 CC BY-SA 3.0 许可协议

这是可能的,但它很丑陋:

 #include <tuple>
#include <iostream>

template<typename T>
void doSomething(T t) { std::cout << t << '\n';}

template<int... N>
struct Switch;

template<int N, int... Ns>
struct Switch<N, Ns...>
{
  template<typename... T>
    void operator()(int n, std::tuple<T...>& t)
    {
      if (n == N)
        doSomething(std::get<N>(t));
      else
        Switch<Ns...>()(n, t);
    }
};

// default
template<>
struct Switch<>
{
  template<typename... T>
    void operator()(int n, std::tuple<T...>& t) { }
};

int main()
{
  std::tuple<int, char, double, int, int, const char*> t;
  Switch<1, 2, 4, 5>()(4, t);
}

只需在 Switch 特化的模板参数列表中列出原始 switch 中可能是 case 标签的每个常量。

要编译, doSomething(std::get<N>(t)) 必须是 Switch 特化的参数列表中每个 N switch 有效表达式…… --- 也有声明。

对于少数情况,它编译为与 switch 相同的代码,我没有检查它是否扩展到大量情况。

如果您不想在 Switch<1, 2, 3, 4, ... 255> 中输入每个数字,那么您可以创建一个 std::integer_sequence 然后使用它来实例化 Switch

 template<size_t... N>
Switch<N...>
make_switch(std::index_sequence<N...>)
{
  return {};
}

std::tuple<int, char, double, int, int, const char*> t;
make_switch(std::make_index_sequence<4>{})(3, t);

这将创建一个 Switch<0,1,2,3> 因此,如果您不想要 0 情况,您需要操纵 index_sequence ,例如,这会将前面的零砍掉名单:

 template<size_t... N>
Switch<N...>
make_switch(std::index_sequence<0, N...>)
{
  return {};
}

不幸的是,GCC 在尝试编译 make_index_sequence<255> 时崩溃,因为它涉及太多递归并使用太多内存,而 Clang 默认也拒绝它(因为它的默认值非常低 -ftemplate-instantiation-depth )所以这不是一个非常实用的解决方案!

原文由 Jonathan Wakely 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题