---
# System prepended metadata

title: begin/end 作業觀念解釋

---

# begin/end 作業觀念解釋

這個作業是想要瞭解對於 C++ 的 *Range* 這個概念 (*concept*) 的理解有沒有正確, 不過也有其他細節是在實作的過程中可以慢慢學習的, 首先 *Range* 的定義是:

> *Range* 是用一對 first & last 迭代器夾住的元素叢集, 迭代器提供的操作像指標, first 在概念上 *指向* 第一個元素, 意即我們可以用敘述 `*first` 來取得第一個元素, 而 last 在概念上指向一個 *虛無的元素*, 這個虛無元素的位置在 **最後一個元素的後方**, 我們不能用 `*last` 來取得這個虛無元素, 但是可以利用 last 作為標兵, 讓 first 在迭代的時候可以用 last 作為判斷是否結束
 
這邊說的迭代器是個概念 (*concept*), 在 C++ 裡 *concept* 這個字眼代表著 **特定的操作集合**, 所以如果我們說某種迭代器 *滿足* (satisfy) [*InputIterator*](https://en.cppreference.com/w/cpp/named_req/InputIterator) 這個 *concept*, 那 **至少** 它有提供連結裡列出的所有操作. 我們在寫函式模版的時候, 最關注型別是否滿足某種 *concept*, 我們寫的每個敘述, 都會逐漸堆疊出一個完整的 **操作集合**, 這個操作集合最終形成我們需要的 *concept*, 而這個 *concept* 就是我們期望型別引數需要 *滿足* 的條件. 

:::info
以這個作業來說不需要另外創造新的類別, 可單純利用指標即可. 因為指標是最簡單而且已經滿足我們需要操作的型別, 即使它實際支援的操作很多, 但我們只要使用必要的操作即可.
:::

在我們學習 *STL Algorithms* 時, 不乏看到對陣列做操作的範例程式碼:

```cpp=
int ia[] = { 2, 1, 3 };

std::sort(ia, ia + 3);
```

這個是透過 *array-to-pointer* 退化 (decay) 轉型取得指標作為迭代器使用, 在 C++ 裡當陣列名稱在被拿來作運算甚至複製的時候, 都會被轉成 *指向第一個元素的指標*, 這是剛好範例會 work 的原因

:::info
`ia + 3` 指向陣列的第 3 個元素, 這個迭代器是做為標兵使用, 不可以拿來取値. 不過 `ia + 3` 其實是屬於 [*RandomAccessIterator*](https://en.cppreference.com/w/cpp/named_req/RandomAccessIterator) 迭代器的操作, 比較好的方式是用 `std::next()` 包裝起來讓它多少有擴充性.
:::

另外在 C++ 語言設計裡, 並 **沒有多維陣列**, 只有 *陣列的陣列* 或 *巢狀陣列* 這樣遞迴的型別, 以 `int[3][2]` 來說, 它是個數為 3 的陣列, 每個元素的型別為 `int[2]`, 這個概念搭配前面講的迭代器標兵, 可以組合產生出一對迭代器:

```cpp=
int iaa[3][2] = {
  { 2, 5 },
  { 1, 6 },
  { 3, 4 },
};

int* const first = &(**iaa);
int* const last  = first + (3 * 2);
std::sort(first, last);
```

以上的 first 指標所代表的含義是: 取得 iaa 第一個整數元素的位址, 這裡不該使用 `reinterpret_cast`, 透過 dereference 操作就可以安全地拿到我們要的値 (型別正確, 內容正確, 沒有 *undifned behavior*); last 指標代表的含義是: 取得從 iaa 第一個整數元素位址往後算起第 6 (3 * 2) 個元素的位址, 因為陣列的性質: 每個元素的位址是連續的且沒有 padding, 所以這樣可以算出標兵的位址. 基本上透過這個概念就可以支援各種巢狀陣列.

因為預期在實作 `begin()` 的時候會遇到和 `std::begin()` 衝突的情況 (標準函式庫版本不支援巢狀陣列), 所以會面臨如何讓自己寫的 `begin()` 和標準函式庫共存的問題.

:::info
想想看迭代器透過統一使用 int* 物件可以帶來什麼好處?
:::