具有可变数量参数的 C++ 非递归函数

C++ non recursive function with variable number of args

我有一个 Matrix 模板 class,我需要一个函数来设置其元素的参数数量可变。

我应该可以这样称呼它:

aghMatrix<string> matrix;
matrix.setItems(2, 3, "torzmiae", "jestdnaci", "tablickore", "wyrazobed", "oelmntai", "rozmiaecy");

其中第一个整数是行数,第二个是列数,其余 (R * C) 参数是我应该放入矩阵的元素。

它应该适用于任何数据类型,而不仅仅是原始数据类型。

目前,我的函数如下所示:

template<typename T>
template<typename... ARGS>
void aghMatrix<T>::setItems(const int rows, const int cols, ARGS... args) {
    array<T, sizeof...(args)>unpacked_args {args...};

    int row = 0;
    int col = 0;
    for (T arg : unpacked_args)
    {
        this->matrixPtr[row][col] = arg;
        col++;
        if (col == this->cols) {
            row++;
            col = 0;
        }
    }

    return;
}

我假设我的矩阵对象能够容纳所有元素。它确实编译了很多关于将所有内容都转换为 unsigned int 的警告,但该程序无论如何都无法运行(它在启动时冻结)。

Class声明:

template<typename T>
class aghMatrix {
public:
    [...]

    template<typename... ARGS> void setItems(const int rows, const int cols, ARGS... args);

    [...]

private:
    T **matrixPtr;
    int rows;
    int cols;

    void createMatrix(const int row, const int col);

    bool checkRowCol(const int row, const int col) const;
};

Github project

编辑: 糟糕!我刚刚注意到您说 "non recursive," 所以我认为以下模式不适合您。我现在仍然会把它挂在这里,但我还在下面提供了一个非递归解决方案(它基于 va_list 因此只适用于 POD 类型)

如果我理解正确你想做什么,那么你可能想要递归可变参数解包模式;这样的事情似乎可以解决问题...

#include <iostream>

using namespace std;

// Helper for build_matrix, taking zero variadic arguments.
// This serves as the termination in the recursive unpacking of the args.
template<typename T>
void build_matrix_helper(T**, size_t, size_t, size_t, size_t) { return; }

// Helper for build_matrix, taking at least one variadic argument.
template <typename T, typename ...ARGS>
void build_matrix_helper(T** matrix, size_t curr_row, size_t curr_col, 
                         size_t row, size_t col, const T& first, ARGS...rest) {
  if (curr_col < col) {
    matrix[curr_row][curr_col] = first;
    ++curr_col;
    return build_matrix_helper<T>(matrix, curr_row, curr_col, row, col, rest...);
  }
  else {
    ++curr_row; 
    curr_col = 0; 
    return build_matrix_helper<T>(matrix, curr_row, curr_col, row, col, first, rest...);
  }
  return;
}

// Bare bones implementation.
template<typename T, typename ...ARGS>
T **build_matrix(size_t row, size_t col, ARGS...elements) {
  T **new_mat = new T*[row];
  for (size_t j = 0; j < row; ++j)
    new_mat[j] = new T[col];

  build_matrix_helper<T>(new_mat, 0, 0, row, col, elements...);
  return new_mat;
}

int main() {
  int **nm = build_matrix<int>(2, 3, 1, 2, 3, 4, 5, 6);

  for (size_t i = 0; i < 2; ++i) {
    cout << "[" << i + 1 << "]: ";
    for (size_t j = 0; j < 3; ++j)
      cout << nm[i][j] << " ";
    cout << endl;
  }

  delete[] nm;
  return 0;
}

一般来说,您希望尽可能避免对内存的任何直接操作。除非绝对需要,否则还要尽可能避免任何强制转换(这也与直接内存操作相关)。

无论如何,可以使用下面的非递归解决方案,使用std::va_list

注意 因为这使用了va_list,它不适用于非 POD 类型。

#include <iostream>
#include <cstdarg>

using namespace std;

template <typename T>
T **build_matrix(size_t row, size_t col, ...) {
  va_list args;
  T **matrix = new T*[row];

  va_start(args, col);

  for (size_t i = 0; i < row; ++i) {
    matrix[i] = new T[col];

    for (size_t j = 0; j < col; ++j)
      matrix[i][j] = va_arg(args, T);
  }

  va_end(args);

  return matrix;
}

int main() {
  int **nm = build_matrix<int>(2, 3, 1, 2, 3, 4, 5, 6);

  for (size_t i = 0; i < 2; ++i) {
    cout << "[" << i + 1 << "]: ";
    for (size_t j = 0; j < 3; ++j)
      cout << nm[i][j] << " ";
    cout << endl;
  }

  delete[] nm;
  return 0;
}

编辑 初始化器列表

正如对您的 OP 的评论中所建议的那样,最好使用初始化列表。我知道这不是您最初要求的,但也许值得考虑:

#include <iostream>
#include <stdexcept>
using namespace std;

template <typename T>
T **build_matrix(size_t row, size_t col, initializer_list<T> il) {
  if (il.size() != row*col)
    throw out_of_range("Number of elements does not match matrix dimensions!");

  size_t curr_row = 0;
  size_t curr_col = 0;
  T **nm = new T*[row];
  nm[0] = new T[col];

  for (T elm : il) {
    if (curr_col == col) {
      ++curr_row;
      nm[curr_row] = new T[col];
      curr_col = 0;
    }
    nm[curr_row][curr_col] = elm;
    ++curr_col;
  }

  return nm;
}

int main() {
  int **nm = build_matrix<int>(2, 3, {1, 2, 3, 4, 5, 6});

  for (size_t i = 0; i < 2; ++i) {
    cout << "[" << i + 1 << "]: ";
    for (size_t j = 0; j < 3; ++j)
      cout << nm[i][j] << " ";
    cout << endl;
  }

  delete[] nm;
  return 0;
}