代码之家  ›  专栏  ›  技术社区  ›  Greg von Winckel

C++ 11中张量的递归索引

  •  1
  • Greg von Winckel  · 技术社区  · 8 年前

    我有一个N阶的张量类,它将存储在数组中的数据包装起来。例如,秩3张量将具有维数(d0、d1、d2),并且将使用来自长度d0×d1×d2的底层数组的多索引(i0、i1、i2)访问唯一元素。如果d0=d1=d2=10,i0=1,i1=2,i2=3,那么将访问数组的元素123。

    我实现了一个递归定义的类,它从多个索引中计算单个数组索引,如下所示:

    template<size_t N>
    class TensorIndex : TensorIndex<N-1> {
    private:
      size_t d;
    public:
    template<typename...Ds>
    TensorIndex( size_t d0, Ds...ds ) : TensorIndex<N-1>( ds... ), d(d0) {}
      template<typename...Is>
      size_t index( size_t i0, Is...is ) {
        return i0+d*TensorIndex<N-1>::index(is...);
      }
    };
    
    template<>
    struct TensorIndex<1> {
    TensorIndex( size_t ) {}
      size_t index( size_t i ) { return i; }
    };
    

    它颠倒了所需的顺序。

    TensorIndex<3> g(10,10,10);
    std::cout << g.index(1,2,3) << std::endl;
    

    输出321。什么是反转构造函数和索引函数的参数顺序的简单方法?

    编辑: 我尝试使用建议的方法反转变量参数来实现,但这是次优的,因为它需要同时反转索引和构造函数的参数,这两种情况下所需的帮助函数看起来略有不同。初始值设定项列表的答案看起来更简单。

    1 回复  |  直到 7 年前
        1
  •  1
  •   Jérôme Migné    7 年前

    不需要递归也不需要反转,可以使用初始值设定项列表调用从左到右累积索引的计算函数。initalizer列表中调用的函数对象应具有非void返回类型:

    #include <cstddef>
    #include <iostream>
    
    using namespace std;
    
    template<size_t N>
    class TensorIndex {
    public:
        template<typename... Args>
        TensorIndex(Args... args) : dims{static_cast<size_t>(args)...}
        {
            static_assert(sizeof...(Args) == N,
                          "incorrect number of arguments for TensorIndex constructor");
        }
    
        template<typename... Args>
        size_t index(Args... args) {
            static_assert(sizeof...(Args) == N,
                          "incorrect number of arguments for TensorIndex::index()");
            IndexEval eval{dims};
            Pass pass{eval(args)...}; // evaluate from left to right : initializer-list                                                                           
            return eval.get_res();
        }
    
    private:
        const size_t dims[N];
    
        class IndexEval {
            size_t k = 0;
            size_t res = 0;
            const size_t* dims;
        public:
            IndexEval(const size_t* dims) : dims{dims} {}
            size_t operator()(size_t i) {
                return res = res * dims[k++] + i;
            }
            size_t get_res() const { return res; }
        };
    
        struct Pass {
            template<typename... Args> Pass(Args...) {}
        };
    };
    
    int main()
    {
        TensorIndex<3> g(10, 10, 10);
        cout << g.index(1, 2, 3) << endl;
    }