- 1.1 更詳細的闡述結構化綁定
- 1.1.1 綁定到匿名對象
- 1.1.2 使用修飾符
- 1.1.3 修飾符并非修飾結構化綁定
- 1.1.4 移動語義
- 1.2 結構化綁定可以在哪使用
- 1.2.1 結構體和類
- 1.2.2 原生數組
- 1.2.3 std::pair,std::tuple和std::array
- 1.3 為結構化綁定提供類似tuple的API
- 1.3.1 隻讀結構化綁定
- 1.3.2 結構化綁定寫操作
1.結構化綁定
結構化綁定:通過對象的元素或成員初始化多個實體。
一個結構體如下:
我們可以将一個
MyStruct
對象綁定到兩個變量上。
在這裡,u和v就是所謂的結構化綁定。将結構體的成員分解初始化了u、v變量。
結構化綁定對于傳回結構或數組的函數特别有用。
例如:傳回一個結構體函數
此時我們可以直接通過結構化綁定拿到結構體的成員。
另一個比較有意思的使用地方在于可以增加代碼可讀性,例如輸出map中所有的鍵值對。map如下:
結構化綁定之前我們周遊給定的是無意義的elem。
有了結構體綁定之後,我們隻需要
[key, val]
。
在這裡我們可以清晰的看出結構化綁定的語義。
1.1 更詳細的闡述結構化綁定
結構化綁定中匿名變量非常重要,結構化綁定引入的新名字的都是指代的這個匿名周遊的成員、元素。
1.1.1 綁定到匿名對象
先考慮如下綁定到匿名變量:
可以看作:
這裡需要注意一點的是,
u
與
v
不是
e.i
與
e.s
的引用。它們隻是這兩個成員的别名。其中e是匿名變量,是以我們不能直接通路
e.i
與
e.s
,但是可以通路
u
與
v
,例如:
這個例子輸出
e.i
與
e.s
的值,它們是
ms.i
和
ms.s
的一份拷貝。
匿名變量
e
的生命周期同結構化綁定的存活時間一樣,當結構化綁定離開作用域時,
e
會同時析構。
在這裡我們還需要了解引用,通過結構化綁定,利用前面的例子:
第一次輸出42: 100,u是ms.i的一份拷貝,而修改了ms.i并不會影響u,是以輸出是42: 100。第二次輸出101:100,直接修改了u,同樣u是ms.i的一份拷貝,修改了不會影響ms.i,是以輸出是101: 100。現在我們換成引用:
此時第一次輸出是100: 100 第二次輸出是101: 101
同理,對傳回值使用結構化綁定,上面規則一樣成立。
轉換為:
可以看到結構化綁定是綁定到一個新的對象,由傳回值進行初始化,而不是直接綁定到傳回值本身。
另外,記憶體位址和對齊也是存在。如果成員有對齊,結構化綁定也會存在對齊,例如:
1.1.2 使用修飾符
結構化綁定中我們可以使用一些修飾符,如:
const
和
&
。
這裡的修飾符是加在匿名變量上。
等價于
引用對結果的影響可以看前面綁定到匿名對象的例子。
1.1.3 修飾符并非修飾結構化綁定
修飾符修飾的是匿名變量,而不是結構化綁定。盡管在結構化綁定的時候會使用到auto,但是結構化綁定的類型不會退化(數組轉指針、修飾符被忽略等)。例如:
調用:
我們檢視a與b的類型發現,還是const char[6]與const char[3],說明并沒有發生退化為指針,原因是修飾符并非修飾結構化綁定而是修飾初始化結構體綁定的對象, 這一點和使用auto初始化新對象很不一樣,它會發生類型退化。
退化為
const char*
。
1.1.4 移動語義
結構化綁定也支援移動語義,例如:
等價于:
結構化綁定u和v指向匿名變量中的成員,該匿名變量是ms的右值引用。ms仍然持有它的值:
此時還可以移動指派u與v,例如v與ms.s關聯。
此時ms.s與v輸出的是未指定的值,s輸出的是Jim。移動後的對象(如前面v)的狀态是有效的,隻是包含了未指定的值(unspecified value)。是以,輸出它的值是沒有問題的,但是不能斷言輸出的東西一定是什麼。
這一點和直接移動ms的值給匿名變量稍有不同:
仍然可以移動n并指派,或者用它賦予一個新的值,但是不會影響ms.s:
1.2 結構化綁定可以在哪使用
原則上,結構化綁定可以應用于public成員、C-style數組、類似tuple對象。具體如下:
- public非靜态成員 結構體或類中的非靜态成員是public
- 原生數組 綁定到每個元素
- 任何類型,使用類似tuple的API
std::tuple_size::value
傳回元素數量
std::tuple_element::type
傳回第idx個元素的類型 一個全局或成員函數
get()
傳回idx個元素的值
使用的時候需要元素或資料成員的數量必須比對結構化綁定的名字的個數。不能跳過任何一個元素,也不能使用同一個名字多次,但是可以使用
_
跳過目前元素。
切記不要在同一命名空間使用多次
_
。
嵌套對象分解是不支援的,諸如:
1.2.1 結構體和類
結構化綁定不适用于繼承,所有非靜态資料成員必須在同一個類。
換句話說,這些資料成員要麼全是基類,要麼全是子類。
例如:
1.2.2 原生數組
數組長度已知的情況下,可以結構化綁定到多個變量上。
C++允許我們傳回帶長度的數組引用:
同樣可以對std::array進行結構化綁定。
1.2.3 std::pair,std::tuple和std::array
結構化綁定可擴充,可以為任何類型添加結構化綁定,标準庫中使用到的有std::pair,std::tuple,std::array
1.std::array
同樣,如果需要更改原生數組裡面的值可以改為:
2.std::tuple
3.std::pair
處理關聯/無序容器的insert()調用的傳回值,使用結構化綁定使代碼可讀性更強,可以更加清晰的表達自己的一圖,而不是依賴與
std::pair
的first與second。
C++ 17之前寫法:
C++17之後:
可以看到C++17提供了一種表達力更強的帶初始化的if。
4.為pair和tuple的結構化綁定指派
聲明了結構化綁定之後,通常不能一次性修改全部結構化綁定,因為結構化綁定是一次性聲明所有。如果重新指派是
std::pair<>
或
std::tuple<>
,可以使用
std::tie()
;
尤其适用于在循環中不停地指派例如下面查找例子:
在text中查找sub
1.3 為結構化綁定提供類似tuple的API
隻要我們的類型實作了類似tuple的API,那麼就可以針對該類型使用結構化綁定,這樣便可以從
std::pair<>
,
std::tuple<>
,和
std::array<>
推廣到任意類型。
1.3.1 隻讀結構化綁定
首先,定義一個類:
提供類似tuple的API:
template <>
struct std::tuple_size {static constexpr int value = 3;
};template <>struct std::tuple_element<2, Customer> {using type = long;
};template <std::size_t Idx>struct std::tuple_element {using type = std::string;
};template <std::size_t>auto get(const Customer& c);template <>auto get<0>(const Customer& c) {return c.getFirst();
}template <>auto get<1>(const Customer& c) {return c.getLast();
}template <>auto get<2>(const Customer& c) {return c.getValue();
}
對get進行封裝,使用編譯時if特性:
有了這些API,結構化綁定就可以在main函數中使用。
輸出:
1.3.2 結構化綁定寫操作
在上述Customer中對成員函數進行修改:
要支援讀寫,需要對常量引用和非常量引用準備getter重載:分别支援非常量對象、常量對象、可移動對象,為了傳回引用,應該使用
decltype(auto)
std::tuple_size<>
與
std::tuple_element<>
不變。
// provide a tuple-like API for class Customer for structured bindings:
template <>
struct std::tuple_size {static constexpr int value = 3; // we have 3 attributes
};template <>struct std::tuple_element<2, Customer> {using type = long; // last attribute is a long
};template <std::size_t Idx>struct std::tuple_element {using type = std::string; // the other attributes are strings
};
同樣,getter方法也可以不用編譯時if,如下:
調用:
輸出:
學習自Cpp17TheCompleteGuide
