C++23 更直覺的多維度資料的存取

| | 0 Comments| 08:51
Categories:

[] 在 C++ 裡面被稱為「subscript operator」,最常使用的情境應該是用來存取資料;尤其在陣列形式的資料中,大部分的人應該都很習慣這樣存取資料了~下面就是一個簡單的使用示意:

std::array<int, 3> a = { 1,2,3 };
a[2] = 4;

這樣的使用實際上就是去呼叫他的 array subscript operator(參考)。

不過,到 C++20 為止,這個運算子都只能接受一個值作為函式引數、所以其實在要存取多維度資料的時候,其實不是很方便。

以一個矩陣來說好了,這邊可能可以定義成:

template<typename T, size_t W, size_t H>
class CMatrix
{
public:
  template<typename ... TI>
  CMatrix(const TI&&... vals) : m_aData{ vals ... } {}
 
  T& data(size_t x, size_t y)
  {
    return m_aData[x + y * W];
  }
 
protected:
  std::array<T, W * H>  m_aData;
};

在這樣的定義下,要存取矩陣裡的其中一項、則可以透過 data() 這個函式來做到:

CMatrix<float, 3, 2> m = {
  1.0f,2.0f,3.0f,
  4.0f,5.0f,6.0f };

std::cout << m.data(0, 1) << "\n";

但是由於 operator[] 只能支援一個引數,所以並沒有辦法寫成 m[0,1] 這樣類似存取陣列的形式。

當然,這邊也是可以透過 operator() 來實作,但是在語意上其實比較容易搞混就是了。


而到了 C++23 則是引進了「multidimensional subscript operator」(P2128R6)、放寬了對於 operator[] 的限制。

所以,在支援這項功能的環境中(目前 Visual C++ 不支援、但是 g+12 已經支援了),就可以改寫成:

template<typename T, size_t W, size_t H>
class CMatrix
{
public:
  template<typename ... TI>
  CMatrix(const TI&&... vals) : m_aData{ vals ... } {}
 
  T& operator[](size_t x, size_t y)
  {
    return m_aData[x + y * W];
  }
 
protected:
  std::array<T, W * H>  m_aData;
};

如此一來,存取這個矩陣的資料,就可以使用 m[0,1] 這樣的形式了。

基本上,這項核心語言上的改變、對於功能面上並沒有明顯的影響,畢竟本來就可以透過其他名稱的函式來實作。

但是這樣的修改可以讓多維陣列的存取和一維陣列更有一致性,在語意上會更明確。
而當然也會希望以後會都統一支援這樣的存取定義,在使用上會更方便。


這邊另外補充一下,以前的 C++ 標準裡面,在 [] 裡面其實是可以有「,」的;只是它實際上是作為「comma operator」在運作、會把左邊的結果略過去、只回傳最後的結果。

所以如果寫 a[0,1] 的話、實際上 0 會被無視、變成 a[1]

而到了 C++20,這樣的寫法也被標記成廢棄、在編譯時會吐警告了。


最後,算是對應這個修改,C++23 在標準函示庫中也加入了 std::span 的多維度版本、std::mdspan參考)。

透過 std::mdspan、可以相當簡單地將一維陣列包裝成多維度的陣列來存取。不過比較可惜的,是目前這東西還沒有哪個編譯器能完整支援就是。

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *