在C++中将不同大小的常量数组作为函数参数传递

5

我有多个不同大小的常量多维数组。我想将它们传递给一个函数。然而,我会得到缺少下标的错误提示,因为数组的大小不同,所以我不能在数组参数中放置下标。这个问题有什么解决方法?

以下是示例代码。实际的数组更大。

//ARRAY1
const double ARRAY1[3][2][2] =
{
    {
        {1.0,1.0},
        {1.0,1.0},
    }
    ,
    {
        {1.0,1.0},
        {1.0,1.0},
    }
    ,
    {
        {1.0,1.0},
        {1.0,1.0},
    }
}
//ARRAY2
const double ARRAY2[2][2][2] =
{
    {
        {1.0,1.0},
        {1.0,1.0},
    }
    ,
    {
        {1.0,1.0},
        {1.0,1.0},
    }
}

//How to declare the parameter?
double SomeFunctionToWorkWithBothArrays(const double arr[][][])
{

}

请只选择一种语言,不要两种都选。C和C++对这个问题的答案会有很大的不同。 - Lundin
8个回答

14

你可以使用模板。

template<size_t first, size_t second, size_t third> 
double SomeFunction(const double (&arr)[first][second][third]) {
    return first + second + third;
}

该函数接受一个三维double数组的引用,其所有维度在编译时已知。如果万不得已,实际上可以通过模板来获取此引用。


谢谢大家的帮助。我会使用模板来完成它。 - Amin1Amin1
@DeadMG :不是很棘手,但那就是我写评论时想到的:P - Nawaz
1
不明白为什么这很棘手。这只是模板的基本使用! - Lightness Races in Orbit
1
需要注意的是,每个函数版本都将获得其自己的函数代码的隐式实例化。 - Martin York
@Martin York:解决方案是从模板封装器内调用非模板函数,并将指针和三个数组大小作为函数参数提交...虽然我不知道多维数组的布局是否由标准定义!如果没有,它将依赖于实现特定行为。 - mmmmmmmm

5

使用std::vector代替数组。向量知道它们自己的大小,所以这不会成为问题。您可以使用向量的向量作为多维数组。


这对于他明显不需要的动态大小来说是相当大的堆开销。 - Puppy
@DeadMG:的确如此,事实上我在这里回答了一个稍有不同的问题。但仍然是我的看法,“使用向量”是一个不错的经验法则。 - Jon
1
可以回答数组向量(即boost/std:: array),如vector<array<array<double, 2>, 2>> - Puppy
实际上,这几乎没有任何开销,动态分配并不强制使用任何“堆”。然而,在可能的情况下坚持使用静态分配确实是首选。 :) - Lightness Races in Orbit

3

您可以使用std::vector(其大小是可变的,不需要在类型中指定),或者您可以坚持使用静态分配并使用模板:

template <size_t X, size_t Y, size_t Z>
double SomeFunctionToWorkWithBothArrays(const double (&arr)[X][Y][Z])
{
   // A different version of this function will
   // exist for each combination of X, Y and Z.
}

(在此示例中,我假设所有三个维度都可能不同。)

还要注意,我是通过引用传递数组的;实际上,您不能按值传递数组,因为参数将折叠为指针,并且对于多维数组,这会变得有点复杂。

希望这可以帮到您。


2
您可以使用boost::array模板类和模板函数声明来处理此问题。
编辑:只是为了添加一个例子:
template<typename T, std::size_t I, std::size_t J, std::size_t K>
void MyFunction(const std::array<std::array<std::array<T, K>, J>, I>& data)
{
    // do something here
}

你将以以下方式调用它:
std::array<std::array<std::array<T, 4>, 2>, 3> data; // which hopefully you have typedef'd somewhere to make the code more readable
MyFunction<double, 3, 2, 4>(data);

1
如果只有第一个维度是可变的(其他维度对于所有数组都相同),那么你仍然可以将这些数组传递给同一个函数,因为在函数参数中允许忽略第一个数组维度。
double SomeFunctionToWorkWithBothArrays(const double arr[][2][2])
{

}

如果其他维度也可以改变,那么你将不得不使用不同的函数,可能是从单个模板创建的,就像@DeadMG的答案中所示。

2
真的,但你应该在这种方法中提到需要将第一维的大小作为参数传递。 - j_random_hacker

1

我知道解决这个问题的两种方法:

  1. 在数组中使用一个“哨兵”值作为最后一个条目,例如{-1.0,-1.0}。您只需要检查下一个主维度中的第一个值。
  2. 向函数添加附加参数,指定维度的大小,例如x、y、z或struct dim { int x, y, z};

此致

Martin.


0

只有在多维数组的排列方式由标准定义(向标准专家提问)时,您才可以执行以下操作:

在您的头文件中:

double SomeFunctionToWorkWithBothArraysInt(double const *data, int x, int y, int z);

template <size_t X, size_t Y, size_t Z>
double SomeFunctionToWorkWithBothArrays(double const (&arr)[X][Y][Z]) {
  SomeFunctionToWorkWithBothArraysInt(&arr, X, Y, Z);
}

在你的 .cpp 文件中:
double SomeFunctionToWorkWithBothArraysInt(double const *data, int x, int y, int z) {
  double res = 0.0;
  for (int xp = 0; xp < x; xp++) {
    for (int yp = 0; yp < y; yp++) {
      for (int zp = 0; zp < z; zp++) {
        res += data[(zp * y + yp) * x + xp]; // !!!ATTENTION!!!: This may be wrong if the array arrangement is different!
      }  
    }  
  }

  return res;
}

优点在于您的“业务逻辑”不会在每个模板函数实例化时实例化,并且不会通过头文件公开。

多维数组的排列是由标准定义的。唯一需要注意的是,您的调用应该是:SomeFunctionToWorkWithBothArraysInt(&arr[0][0][0], X, Y, Z)。地址的值将不会改变,但类型现在将是“指向const double的指针”,而不是“指向double数组(大小为Y)的数组(大小为Z)的指针”或“指向double数组(大小为X)的数组(大小为Y)的数组(大小为Z)的指针”(我记不清哪个了)。 - Martin Bonner supports Monica

0
在C语言中,只需将您的函数定义为:
double func (size_t x, size_t y, size_t z, double arr[x][y][z])

这是可能的,因为C语言(不像C++)具有可变长度数组。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接