--- tags: 自學 --- # Structured binding declaration ###### tags: `C++17` `cppreference` Structured binding 的宣告有以下三種 - attr(optional) cv-auto ref-qualifier(optional) \[identifier-list\] = expression; - attr(optional) cv-auto ref-qualifier(optional) \[identifier-list\]{expression}; - attr(optional) cv-auto ref-qualifier(optional) \[identifier-list\](expression); Description: - attr: 標籤的序列,詳見[cppreference](https://en.cppreference.com/w/cpp/language/attributes) - cv-auto: `cv`代表的是`cv-qualified type`,意思是`const`或者`volatile`,這裡的`auto`可能是`const`或者`volatile`。(從C++20開始,這裡的`auto`可能是`const`, `static`或者`thread_local`,不再包含`volatile`) - ref-qulifier: `&`或者`&&` - identifier-list: 用逗號分隔的變數名稱 - expression: 在最上層沒有逗號的表達式,而且有array或者不是`union`的資料型態。如果這個 expression 是指向任意一個 identifier-list 中的identifier,那這個宣告是 **ill-formed**的。 Structured binding declaration 在 identifier-list 中宣告了所有的變identifier,並且將每個 identifier 綁定到 expression 中的元素或子物件中。這樣引入的綁定稱為結構化綁定。 以下用$E$表示 expression(e) 的資料型態,因此,$E$與`std::remove_reference_t<decltype((e))>`相同。 Structured binding declaration 根據$E$的型態,提供下列三種不同的綁定 - Case 1: $E$ 是 array type - Case 2: $E$ 不是`union`型態,並且`std::tuple_size<E>`是一個完整的型態 - Case 3: $E$ 不是`union`型態,並且`std::tuple_size<E>`不是一個完整的型態 ## 使用情境 ### Case 1 Array 每個 identifier-list 中的identifier 作為`lvalue`對應到 array 中的元素,兩邊的元素個數應該一致。 每個identifier 的引用型態等於array元素的型態,要注意若$E$有`cv`,則變數也會有這些`cv`。 ```cpp= int a[2] = {1, 2}; auto [x, y] = a; // x = a[0], y = a[1] auto& [xr, yr] = a; // xr refer to a[0] and yr refer to a[1] x = 3; // a = {1, 2} xr = 3; // a = {3, 2} ``` ### Case 2 Tuple-like Type `std::tuple_size<E>::value`必須是**well-formed**的不變整數,identifier 的數量應該與`std::tuple_size<E>::value`相等。 對於每個型態是`reference to std::tuple_element<i, E>::type`的identifier,若他的 initializer 是`lvalue`,則為`lvalue reference`,否則為`rvalue reference`。i-th variable的 initializer為: - `e.get<i>()`: 找到至少一個function template宣告,且第一個 template parameter不是型態參數的 function template。 - `get<i>(e)`: Otherwise。get只會用[ADL](https://en.cppreference.com/w/cpp/language/adl)查找。 在這些 initializer中,若$e$是`lvalue reference`(這只會在 ref-qualifier是&或者是&&且是lvalue時發生)則`e`是`lvalue`,除此之外都是[xvalue](https://en.cppreference.com/w/cpp/language/value_category#xvalue),`i`是一個 `std::size_t` [prvalue](https://en.cppreference.com/w/cpp/language/value_category#prvalue),而`<i>`則都是在表示一個 template parameter list。 每個變數都跟$e$有相同的[storage duration](https://en.cppreference.com/w/cpp/language/storage_duration)。每個identifier 變成左值的名稱,該左值引用綁定到變數的物件。第i個identifier的引用型態是`std::tuple_element<i, E>::type` ```cpp= float x{}; char y{}; int z{}; std::tuple<float&, char&&, int> tpl(x, move(y), z); // tpl = {0, ' ', 0} const auto& [a, b, c] = tpl; // a = 0, b = ' ', c = 0 a = 10; // tpl = {10, ' ', 0}, x = 10, y = ' ', z = 0 b = 'a'; // tpl = {10, 'a', 0}, x = 10, y = 'a', z = 0 c = 20; // error; 宣告的是const,因此c不能重新附值 // 除非tuple中,他也是refer到其他變數 /* using Tpl = const std::tuple<float&, char&&, int>; a names a structured binding that refers to x (initialized from get<0>(tpl)) decltype(a) is std::tuple_element<0, Tpl>::type, i.e. float& b names a structured binding that refers to y (initialized from get<1>(tpl)) decltype(b) is std::tuple_element<1, Tpl>::type, i.e. char&& c names a structured binding that refers to the third component of tpl, get<2>(tpl) decltype(c) is std::tuple_element<2, Tpl>::type, i.e. const int */ ``` ### Case 3 Data Members identifier 的數量必須與 non-static data members的數量相同,且$E$不能是`union`型別。每個identifier 就是物件成員的別稱。 ```cpp= struct S{ mutable int x1:2; double y1; }; S f(){return S{1, 2.3};} int main(){ const auto [x, y] = f(); // x = 1, y = 2.3 x = -1; // x = -1, y = 2.3 // 由於在結構S中的x1為mutable // 因此即便x被宣告為const,仍然可以改變 y = -2; // error; 宣告的是const,因此不能重新附值 } /* Another example */ struct S2{ int &x2; double &y2; }; int a = 1; double b = 2.3; S2 g(){return S2{a, b};} int main(){ const auto [x, y] = g(); // x = 1, y = 2.3 // x refer to a, y refer to b x = -1; // a = -1, b = 2.3 y = -2.3; // a = -1, b = -2.3 } ``` ## 參考資料 - [cppreference](https://en.cppreference.com/w/cpp/language/structured_binding) Structured binding declaration (since C++17)