繁体   English   中英

数组大小可变的模板类:使用数组引用或括号括起来的初始化程序列表进行初始化

[英]Template class with variable array size: initialize with array reference or brace-enclosed initializer list

考虑以下类别:

template <size_t nb_rows, size_t nb_cols>
class ButtonMatrix
{
  public:
    ButtonMatrix(const pin_t (&rowPins)[nb_rows], const pin_t (&colPins)[nb_cols], const uint8_t (&addresses)[nb_rows][nb_cols])
      : rowPins(rowPins), colPins(colPins), addresses(addresses) { }
  private:
    const pin_t (&rowPins)[nb_rows], (&colPins)[nb_cols];
    const uint8_t (&addresses)[nb_rows][nb_cols];
};

我可以使用如下数组初始化它:

const pin_t rows[2] = {0, 1};
const pin_t cols[2] = {2, 3};

const uint8_t addresses[2][2] = {
  {0x01, 0x02},
  {0x03, 0x04}
};

ButtonMatrix<2, 2> bm(rows, cols, addresses);

而且效果很好。 但是,我也希望能够使用括号括起来的初始化器列表来初始化它:

ButtonMatrix<2, 2> bm({0, 1}, {2, 3}, addresses);

它可以毫无问题地进行编译,但是显然行不通,因为rowPinscolPins仅在构造函数期间有效,并且不能在该类的其他方法中使用。 为了解决这个问题,我可以复制rowPinscolPins的内容:

template <size_t nb_rows, size_t nb_cols>
class ButtonMatrix
{
  public:
    ButtonMatrix(const pin_t (&rowPins)[nb_rows], const pin_t (&colPins)[nb_cols], const uint8_t (&addresses)[nb_rows][nb_cols])
      : addresses(addresses) {
      memcpy(this->rowPins, rowPins, sizeof(rowPins[0]) * nb_rows);
      memcpy(this->colPins, colPins, sizeof(colPins[0]) * nb_cols);
    }
  private:
    pin_t rowPins[nb_rows], colPins[nb_cols];
    const uint8_t (&addresses)[nb_rows][nb_cols];
};

这样,我可以使用数组引用或用大括号括起来的初始化程序列表对其进行初始化。
唯一的缺点是使用数组引用时,同一数据有两个副本。 目标平台是Arduino,因此我想将内存使用量降至最低。
有没有一种方法可以确定是否使用了初始化程序列表来初始化它,如果是,则动态地确定数组的内存?
遵循以下原则: 具有const数组和initializer_list的C ++重载构造函数

如果初始值设定项列表的尺寸不匹配nb_rowsnb_cols ,则发生编译时错误会很好,因此可以排除std::initializer_list (如果我输入错误,请更正)。

如果我们可以用std::array替换裸std::array (它们更容易为多维数组动态分配),则部分解决方案是依靠以下事实:临时变量更喜欢作为右值引用传递:

template <typename T>
void no_deleter(T*) { }

template <typename T>
void default_deleter(T *p) {
    delete p;
}

template <size_t nb_rows, size_t nb_cols>
class ButtonMatrix
{
  public:
    using row_array_t = std::array<pin_t, nb_rows>;
    using col_array_t = std::array<pin_t, nb_cols>;
    using address_array_t = std::array<std::array<uint8_t, nb_cols>, nb_rows>;

  private:
    template <typename T>
    using array_ptr = std::unique_ptr<T, void (*)(T*)>;

  public:
    ButtonMatrix(const row_array_t &rowPins, const col_array_t &colPins, const address_array_t &addresses)
      : rowPins(&rowPins, no_deleter<const row_array_t>),
        colPins(&colPins, no_deleter<const col_array_t>),
        addresses(&addresses, no_deleter<const address_array_t>) { }

    ButtonMatrix(row_array_t &&rowPins, col_array_t &&colPins, address_array_t &&addresses)
      : rowPins(new row_array_t(std::move(rowPins)), default_deleter<const row_array_t>),
        colPins(new col_array_t(std::move(colPins)), default_deleter<const col_array_t>),
        addresses(new address_array_t(std::move(addresses)), default_deleter<const address_array_t>) { }

  private:
    array_ptr<const row_array_t> rowPins;
    array_ptr<const col_array_t> colPins;
    array_ptr<const address_array_t> addresses;
};

现在,当您执行此操作时,传递的静态数组将由指针存储,并且不会被删除:

ButtonMatrix<2, 2>::row_array_t rows{{0, 1}};
ButtonMatrix<2, 2>::col_array_t cols{{2, 3}};

ButtonMatrix<2, 2>::address_array_t addresses = {{
  {{0x01, 0x02}},
  {{0x03, 0x04}}
}};

ButtonMatrix<2, 2> bm(rows, cols, addresses);

但是,如果您以内联方式传递所有内容,则会将副本复制到堆分配的数组中,该数组确实会被正确删除:

ButtonMatrix<2, 2> bm2({{0, 1}}, {{2, 3}}, {{{{1, 2}}, {{3, 4}}}});

(请注意,由于std::array构造函数的工作方式,您需要在所有内容上加上大括号。)


但是,这也有点危险,因为临时对象也可以绑定到const引用 -如果在同一构造函数调用中混合使用静态数据和临时数据,则将调用第一个构造函数,并且您将存储指向临时对象的指针离开 不好。

更好的解决方案(允许在同一构造函数调用中同时包含静态数据和临时数据)将需要大量模板魔术。 我可能会尝试解决这个问题,但是我可能没有时间。

即使有点冗长,我还是可以通过一个解决方案在呼叫站点指定是否必须复制参数。 证明您没有存储指向临时对象的长寿指针将更加安全,而且更加容易。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM