【问题标题】:Unrolling loops using templates in C++ with partial specialization在具有部分特化的 C++ 中使用模板展开循环
【发布时间】:2011-03-04 00:45:37
【问题描述】:

我正在尝试使用模板在 C++ 中展开循环,如下所示。

#include <iostream>

template< class T, T i >
struct printDown {
    static void run(void) {
        std::cout << i << "\n";
        printDown< T, i - 1 >::run();
    }
};

template< class T >
struct printDown< T, 0 > {
    static void run(void) {
        std::cout << 0 << "\n";
    }
};

int main(void) {
    printDown< int, 10 >::run();
    return 0;
}

当我在 Cygwin 中使用 g++ 3.4.4 编译时,出现以下错误。

tmp.cpp:12: error: type T' of template argument0' 取决于 模板参数

我做错了什么?我需要以某种方式注释 0 以说明它是 T 类型吗?

提前致谢。

【问题讨论】:

    标签: c++ templates specialization partial-specialization loop-unrolling


    【解决方案1】:

    您是否尝试过int i 而不是T i?

    【讨论】:

    • (我的意思是:省略class T,对第一个特化使用单个模板参数int i,对于零大小写使用空template &lt;&gt;。)
    • @phooji:我宁愿推荐一个无符号整数(随便你)
    • @Matthieu M:我不确定你的意思——你是说unsigned 更合适(因为我们通常倒数到零)?
    • @phooji:我的意思是考虑到在他的特定实现中,他正在向 0 计数,使用负值会对编译造成严重破坏,因此建议切换到无符号整数。跨度>
    • @Matthieu M:是的,但我认为对于大多数编译器来说,整数值(有符号或无符号)30 会同样糟糕(即,它无法实例化模板表达式)。换句话说,unsigned 会产生同样多的无效值。
    【解决方案2】:

    为什么会这样?从 14.5.5/8 开始,

    ——模板参数的类型 对应一个专门的 非类型参数不应 取决于参数 专业化。 [ 例子:

    template <class T, T t> struct C {};
    template <class T> struct C<T, 1>; // error
    template< int X, int (*array_ptr)[X] > class A {};
    int array[5];
    template< int X > class A<X,&array> { }; // error
    

    ——结束示例]

    因此,当您应用部分特化时,0 的类型是 T(取决于特化的参数)。有两种选择,一是使其不依赖,例如将 T i 更改为 int i,二是应用显式特化而不是部分特化。

    这两个解决方案都是别人给出的,所以我不会在这里转发。至少你知道原因。它是由标准定义的。

    【讨论】:

    • 感谢您的解释。我不知道这个规则。问题是使非递归案例“struct printDown”意味着该类型专门用于无符号,或者如果我将其更改为 int。但这并不普遍。我想将其概括为任何可以从中减去 1 并且可以与 0 进行比较的类型。我怎样才能做到这一点?
    【解决方案3】:

    正如phooji 所指出的,您的实现存在一个小问题:它会快速生成一长串调用,这会使编译器很快窒息。

    您可以通过使用二进制分解实现稍微复杂的版本来解决此问题。我也会让它在仿函数上通用,因为我很懒。

    // Signature
    template <Functor F, unsigned N>
    struct UnrolledLoop;
    

    我们需要一个辅助模板,它保留要传递的参数的偏移量

    template <Functor F, unsigned N, unsigned OffSet>
    struct UnrolledImpl;
    
    template <Functor F, unsigned OffSet>
    struct UnrolledImpl<F, 0, OffSet>
    {
      static F run(F f) { return f; }
    };
    
    template <Functor F, unsigned OffSet>
    struct UnrolledImpl<F, 1, OffSet>
    {
      static F run(F f) { f(OffSet); return f; }
    };
    
    template <Functor F, unsigned N, unsigned OffSet>
    struct UnrolledImpl
    {
      static F run(F f) {
        F f2 = UnrolledImpl<F, N/2, OffSet>::run(f);
        return UnrolledImpl<F, N - N/2, OffSet + N/2>::run(f2);
      }
    };
    

    您可以简单地实现UnrolledLoop:

    template <Functor F, unsigned N>
    struct UnrolledLoop
    {
      static F run(F f) { return UnrolledImpl<F, N, 0>::run(f); }
    }
    

    请注意,您可以为 N 的更多值(例如 3、4)提供专门化,以便在编译器上更好。

    【讨论】:

    • +1 也许不是恭维,但你是我的疯子 :) 在这一点上,可能值得指出 Boost MPL:boost.org/doc/libs/1_46_0/libs/mpl/doc/index.html。
    • @phooji:我会把它当作一种恭维 :) 我喜欢摆弄 MPL、Fusion... 和预处理器 ^^ 不过我在工作中必须克制自己:/
    【解决方案4】:

    如何将其添加到您的示例中:

    template struct printDown< int, 0 >{
        static void run(void) {
        std::cout << 0 << "\n";
    } };
    

    如果事先不知道 T 的类型,编译器不能自动将 0 转换为 int。

    【讨论】:

      【解决方案5】:

      刚刚发现。显然可以做这样的事情。

      template< class T, T i, bool b = (i == 0) >
      struct printDown {
          static void run(void) {
              std::cout << i << "\n";
              printDown< T, i - 1 >::run();
          }
      };
      
      template< class T, T i >
      struct printDown< T, i, true > {
          static void run(void) {
              std::cout << 0 << "\n";
          }
      };
      

      我不知道可以这样做。非常Prologish,非常好。

      【讨论】:

        【解决方案6】:

        您可以将参数设为类型参数来解决此问题

        template< bool > struct bool_ { };
        
        template< class T, T i, typename = bool_<true> >
        struct printDown {
            static void run(void) {
                std::cout << i << "\n";
                printDown< T, i - 1 >::run();
            }
        };
        
        template< class T, T i >
        struct printDown< T, i, bool_<i == 0> > {
            static void run(void) {
                std::cout << 0 << "\n";
            }
        };
        
        int main(void) {
            printDown< int, 10 >::run();
            return 0;
        }
        

        这样你可以在偏特化中指定你想要的任何条件。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-06-10
          相关资源
          最近更新 更多