2020年5月13日

[C++] Structure Binding in C++17

用一句最簡單的畫作結論的話就是語法糖
C++11 開始除了一些新觀念的引進 (比方說 move semantic) 之外,類似的語法糖也增加了不少,經典例子就是 lambda expression。而 structure binding 到底是啥呢? 舉個簡單例子:
std::pair<int, double> ms{1, 3.14};
auto [i, d] = ms;
一個很樸實無華但是稍微可以讓你的 code 更簡潔易懂的東西。
正如上面例子看到的,沒有 structure binding 的話,同樣的語意要寫成下面這樣:
std::pair<int, double> ms{1, 3.14};
auto i = ms.first;
auto d = ms.second;
實際上的行為也確實等價,所以才說是語法糖 (類似語法在 python 之類的語言也有)。
要特別注意的是這裡面確實有牽涉到 "複製" 變數值的行為,換言之如果以上面的例子來說,std::pair element type 是像 std::string 這種比較龐大的 object,這時候就要注意是否會產生非預期的成本。

當然,雖然例子上是用 auto,其實也可以取代成 const auto / auto& / auto&& 等等,來宣告變數的型態,最後變數推導後的型態跟 auto type deduction 有異曲同工之妙,這邊就不再贅述。不同之處的話在於一般來說 auto type deduction 會有 type decay 的現象產生 (比方說 array type 會 decay 成 pointer type:char[6] decay 後會是 char*),在 structure binding 這邊則不會有 decay,會直接使用 element type:

struct S {
    char x[6];
    char y[3];
};
S s1{};
auto [a, b] = s1; // types of a/b are char[6] / char[3]
auto s = s1.x;    // type of s: char*
上面的例子應該就能看出跟一般的 auto type deduction 的差異。
另一個值得注意的點是 structure binding 也能用在 array type (必須是能知道 array size 的 array type,像是上面的 char[3] 這種),所以其實還能這樣寫:
int arr[] = {1, 2};
auto [x, y] = arr; // OK
auto [z] = arr; // Error: number of elements does not match
實用與否個人覺得很微妙,偏向不怎麼有用,不過基本上是同樣概念的推演。而這個概念還能進一步推演到 tuple 或是 tuple-like 的型別都可 (只要能把幾個操作 tuple 時會用的 template 作全特化就能用),所以實際上能應用的範圍極廣。但就目前看到的幾個說法,最大的應用點應該是 STL 的 iterator,特別是 STL container 做 insert 這類操作後的有些 overloading 的回傳值其實是一個 std::pair,用 structure binding 可以用簡潔的寫法同時拿到更新後的 iterator 以及此次操作是否成功的回傳值。

結論:這種語法糖是否使用確實是看人,也不是多難懂的語法,所以我自己認為在顧慮後人看不懂的這點來說不是個大問題 (不像 lambda expression 就有點複雜...)

沒有留言:

張貼留言