首页 > 解决方案 > 如何在编译时在 C++ 中将维度矩阵中的位置映射到一维?

问题描述

我必须n在编译期间计算一维数组内维矩阵中元素的位置。简而言之,每个索引的array[i][j][k]....[n] -> array[...], where i,j,k,...,nin和维度的映射是。这意味着,每一行和每一列都有 3 个元素。{1,2,3}DIM = 3

我的主要问题是编写n索引(参数包)的总和,作为模板并constexpr用于在编译时评估总和。

我在其他堆栈帖子中的研究得出了以下 3 维公式:

    a[i][j][k] -> a[(i*DIM*DIM) + (j*DIM) + k]

如果我们将其扩展为n维度,则会得到以下公式:

    a[i][j][k]....[n] -> a[(n*DIM ^ (indexAmount-1)] +... + (i*DIM*DIM) + (j*DIM) + k].

此外,i使用模板和 编写代码来生成和的加数,constexpr如下面的代码所示。

    /**
    * calculates DIM3^(indexPos)
    */
    template<auto count>
    int constexpr multiple_dim(){
        if constexpr (count == 0){
            return 1;
        }else{
            return DIM3 * multiple_dim<count-1>();
        }
    }

    /**
    *
    *calculates addends for the end summation
    * e.g if we have 3 indices i,j,k. j would be at position 2
    * and j = 1. The parameters would be IndexPos = 2, index = 1.

    */
    template<auto indexPos, auto index>
    int constexpr calculate_flattened_index(){
        if constexpr (indexPos == 0){
            return (index-1);
        }else{
            return (index-1) * multiple_dim<indexPos>();
        }
    }

    /**
     * calculates the position of an element inside a
     * nD matrix and maps it to a position in 1D
     * A[i][j]..[n] -> ???? not implemented yet
     * @tparam Args
     * @return
     */
    template<auto ...Args>
    [[maybe_unused]] auto constexpr pos_nd_to_1d(){
     /* maybe iterate over all indices inside the parameter pack?
        const int count = 1;
        for(int x : {Args...}){

        }
        return count;
    */
    }

3D 矩阵中元素的示例输出AA111, A121, A131. 3 个元素的总和将是 1D 中的位置。例如A121 -> 0 + 3 + 0 = 3A111将被放置在 的一维数组中array[3]

    std::cout << "Matrix A111" << std::endl;
    //A111
    std::cout << calculate_flattened_index<0 , 1>() << std::endl;
    std::cout << calculate_flattened_index<1 , 1>() << std::endl;
    std::cout << calculate_flattened_index<2 , 1>() << std::endl;
    std::cout << "Matrix A121" << std::endl;
    //A121
    std::cout << calculate_flattened_index<0 , 1>() << std::endl;
    std::cout << calculate_flattened_index<1 , 2>() << std::endl;
    std::cout << calculate_flattened_index<2 , 1>() << std::endl;
    std::cout << "Matrix A131" << std::endl;
    //A131
    std::cout << calculate_flattened_index<0 , 1>() << std::endl;
    std::cout << calculate_flattened_index<1 , 3>() << std::endl;
    std::cout << calculate_flattened_index<2 , 1>() << std::endl;

    Output:
    Matrix A111
    0
    0
    0
    Matrix A121
    0
    3
    0
    Matrix A131
    0
    6
    0

所需的输出可能类似于以下代码:

函数调用

    pos_nd_to_1d<1,1,1>() //A111 
    pos_nd_to_1d<1,2,1>() //A121 
    pos_nd_to_1d<1,3,1>() //A131 

输出:

    0 //0+0+0
    3 //0+3+0
    6 //0+6+0

标签: templatesmatrixc++17variadic-templatestemplate-meta-programming

解决方案


如果我理解正确...您看起来如下

template <auto ... as>
auto constexpr pos_nd_to_1d ()
 { 
   std::size_t  i { 0u };

   ((i *= DIM, i += as - 1u), ...);

   return i;
 }

或者,也许您可​​以使用std::common_type, for i,

std::common_type_t<decltype(as)...>  i {};

但对于索引,我建议使用std::size_t(also std::size_t ... as)。

下面是一个完整的编译示例

#include <iostream>

constexpr auto DIM = 3u;

template <auto ... as>
auto constexpr pos_nd_to_1d ()
 { 
   std::size_t  i { 0u };

   ((i *= DIM, i += as - 1u), ...);

   return i;
 }


int main ()
 {
   std::cout << pos_nd_to_1d<1u, 1u, 1u>() << std::endl;
   std::cout << pos_nd_to_1d<1u, 2u, 1u>() << std::endl;
   std::cout << pos_nd_to_1d<1u, 3u, 1u>() << std::endl;
 }

- 编辑 -

OP问

你能解释一下这段代码是如何工作的吗?我对 C++ 有点陌生。

无论如何,我更擅长编写代码来解释......

我在这里用过的

   ((i *= DIM, i += as - 1u), ...);
//...^^^^^^^^^^^^^^^^^^^^^^   repeated part

被称为“折叠表达式”(或也称为“折叠”或“模板折叠”),并且是 C++17 的一项新功能(您也可以在 C++14 中获得相同的结果(也是 C++11 但不是constexpr)但以一种不太简单和优雅的方式)包括使用运算符扩展可变参数模板包。

例如,如果你想对索引求和,你可以简单地写

(as + ...);

并且表达式变为

(a0 + (a1 + (a2 + (/* etc */))));

在这种情况下,我使用了逗号是运算符这一事实,因此表达式

   ((i *= DIM, i += as - 1u), ...);

变得

   ((i *= DIM, i += a0 - 1u), 
     ((i *= DIM, i += a1 - 1u),
        ((i *= DIM, i += a2 - 1u),
           /* etc. */ )))))

观察到,这样,第一个i *= DIM是无用的(因为i初始化为零),但以下i *= DIM乘以as - 1u正确的次数

所以,什么时候as...1, 2, 1例如,你得到

  (1 - 1)*DIM*DIM + (2 - 1)*DIM + (1 - 1)

推荐阅读