【问题标题】:Forcing template member function to instantiate强制模板成员函数实例化
【发布时间】:2015-11-28 22:05:19
【问题描述】:

我在命名空间 N 中有一个类 C,其公共模板成员函数 F 如下:

namespace N {
    class C {
    public:
        template<int I>
        void F() {
            // ...
        }
    };
};

N::C::F&lt;I&gt;I 的值直到运行时才知道。但是,I 的值受到限制,使得0 &lt;= I &lt; 2^8。我正在寻找一种方法来强制此函数将模板扩展到所有 256 种可能的形式。

到目前为止,我已经在 C 的第二个函数中手动创建了一个静态数组,它指向每个可能的函数:

        template<int I>
        void F() {
            // ...
        }

        void G(int I) {
            static void(* const funcs[256])() = {
                F<0>, F<1>, F<2>, ...
            };

            funcs[I]();
        }

虽然我想知道是否有更好的方法。我已经在N 中有一个宏,它负责为I 的每个值构造一个单独的结构(供F 本身使用),并且正在寻找是否可以在其中以某种方式集成模板成员函数的实例化:

    template<int> struct S;

    #define M(I, n) \
        template<> struct S<I> { \
            static const char name[] = #n; \
            /*
                Some how instantiate the function here, like (just guessing here):

                static const SEvaluator<I> eval = &C::F<I>;

                given

                template<int I>
                using SEvaluator = void(*)();
            */
        };

    M(0, "foo"); M(1, "bar");

    #undef M

我提出的方法不能按原样工作,编译器抱怨 F 不是 constexpr。 F 操作C 的几个变量并调用外部方法,无法声明为 constexpr。有没有办法挽救这个问题,还是我必须求助于我的第一个黑客方法?

【问题讨论】:

  • 有点绕你的问题,但I不能只是一个普通的函数参数吗?通过生成 256 个函数版本并支付间接分支的成本(在运行时确定实际值时这是不可避免的),我看不到您可能获得的太多好处,只是为了允许基于已知的 I 进行一些优化编译时间。它似乎是用更小的开销换取更大的开销。就像F 中的手动展开循环一样?那种东西应该更直接,并带来更多的回报。
  • @Ike 对于上下文,F 执行各种操作码I 的逻辑。虚拟机将遍历 ROM 并在当前字节上调用 F。 F 由一堆 if 语句组成,检查给定的 I 是否具有给定的寻址模式、功能等。目前我有这些 if 语句都针对每个操作码进行程序评估,但我认为将逻辑划分为单独的分支-less 函数和创建跳转表会带来性能提升。 (1/2)
  • 如果我理解正确的话,就性能而言,拥有更多函数的二进制开销和额外间接分支的评估开销将超过评估一堆不必要的 if 语句的开销。我不能说我对低级优化有很多了解,所以这种方法至少在我的脑海中是有意义的,但是你建议我如何改进呢? (2/2
  • 我明白了——如果你正在有效地模拟一个跳转表并且为F 生成的代码本质上是相对较小和/或非常多枝的(内部有很多if/else),它可能非常值得寻求这个。我有点担心的只是这个跳转表的大小和生成的潜在代码量以及内联功能的潜在损失(如果F 很小)或 icache 问题(如果F 很大)-我们可能会失去比我们在那里获得的更多。我很好奇它的表现如何——这是一个非常有趣的例子,更多的代码和一个广泛的分支与更少的代码和更小的分支。

标签: c++ templates c++11 instantiation


【解决方案1】:

您可以使用index_sequence&lt;I...&gt; (C++14) 并将I 扩展为静态数组。

template<std::size_t... Is>
void G(int i, std::index_sequence<Is...>) {
    using C = void(C::*)();
    static C funcs[sizeof...(Is)] = { &C::F<Is>... };
    (this->*funcs[i])();
}

void G(int i) {
    G(i, std::make_index_sequence<256>());
}

对于非 C++14 解决方案,您可以编写自己的 index_sequence 版本:

template<int... Is>
struct index_sequence { };

template<int N, int... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> { };

template<int... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> { };

【讨论】:

    【解决方案2】:

    显然我被蝙蝠侠打败了。无论如何我都会发布这个:原理是一样的,只是换一种方式。

    template <std::size_t... Idx>
    auto const &makeFunctions(std::index_sequence<Idx...>) {
        static auto funcs = {&N::C::F<Idx>...};
        return funcs;
    }
    
    auto const &function(std::size_t i) { return *(begin(makeFunctions(std::make_index_sequence<256>{})) + i); }
    

    致电:

    N::C c;
    for(int i = 0; i < 10; ++i)
        (c.*function(i))();
    

    Live on Coliru

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多