簡體   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