Try   HackMD

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
  • 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元素的型態,要注意若

Ecv,則變數也會有這些cv

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查找。

在這些 initializer中,若

elvalue reference(這只會在 ref-qualifier是&或者是&&且是lvalue時發生)則elvalue,除此之外都是xvaluei是一個 std::size_t prvalue,而<i>則都是在表示一個 template parameter list。

每個變數都跟

e有相同的storage duration。每個identifier 變成左值的名稱,該左值引用綁定到變數的物件。第i個identifier的引用型態是std::tuple_element<i, E>::type

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 就是物件成員的別稱。

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 }

參考資料