0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

聊聊結(jié)構(gòu)化綁定

CPP開發(fā)者 ? 來(lái)源:高性能架構(gòu)探索 ? 2023-08-03 15:34 ? 次閱讀
動(dòng)機(jī)

std::mapinsert方法返回std::pair,兩個(gè)元素分別是指向所插入鍵值對(duì)的迭代器與指示是否新插入元素的布爾值,而std::map::iterator解引用又得到鍵值對(duì)std::pair。在一個(gè)涉及std::map算法中,有可能出現(xiàn)大量的firstsecond,讓人不知所措。

#include
#include

intmain()
{
typedefstd::mapMap;
Mapmap;
std::pairresult=map.insert(Map::value_type(1,2));
if(result.second)
std::cout<first<second<

C++11標(biāo)準(zhǔn)庫(kù)添加了std::tie,用若干引用構(gòu)造出一個(gè)std::tuple,對(duì)它賦以std::tuple對(duì)象可以給其中的引用一一賦值(二元std::tuple可以由std::pair構(gòu)造或賦值)。std::ignore是一個(gè)占位符,所在位置的賦值被忽略。

#include
#include
#include

intmain()
{
std::mapmap;
boolinserted;
std::ignore,inserted)=map.insert({1,2});
if(inserted)
std::cout<

但是這種方法仍遠(yuǎn)不完美,因?yàn)椋?/p>

?變量必須事先單獨(dú)聲明,其類型都需顯式表示,無(wú)法自動(dòng)推導(dǎo);?對(duì)于默認(rèn)構(gòu)造函數(shù)執(zhí)行零初始化的類型,零初始化的過程是多余的;?也許根本沒有可用的默認(rèn)構(gòu)造函數(shù),如std::ofstream。

為此,C++17引入了結(jié)構(gòu)化綁定(structured binding)。

#include
#include

intmain()
{
std::mapmap;
auto&&[iter,inserted]=map.insert({1,2});
if(inserted)
std::cout<

結(jié)構(gòu)化綁定這一語(yǔ)言特性在提議的階段曾被稱為分解聲明(decomposition declaration),后來(lái)又被改回結(jié)構(gòu)化綁定。這個(gè)名字想強(qiáng)調(diào)的是,結(jié)構(gòu)化綁定的意義重在綁定而非聲明。

語(yǔ)法

結(jié)構(gòu)化綁定有三種語(yǔ)法:

attr(optional)cv-autoref-operator(optional)[identifier-list]=expression;
attr(optional)cv-autoref-operator(optional)[identifier-list]{expression};
attr(optional)cv-autoref-operator(optional)[identifier-list](expression);

其中,attr(optional)為可選的attributescv-auto為可能有constvolatile修飾的auto,ref-operator(optional)為可選的&&&,identifier-list為逗號(hào)分隔的標(biāo)識(shí)符,expression為單個(gè)表達(dá)式。

另外再定義initializer= expression{ expression }( expression ),換言之上面三種語(yǔ)法有統(tǒng)一的形式attr(optional) cv-auto ref-operator(optional) [ identifier-list ] initializer;。

整個(gè)語(yǔ)句是一個(gè)結(jié)構(gòu)化綁定聲明,標(biāo)識(shí)符也稱為結(jié)構(gòu)化綁定(structured bindings),不過兩處“binding”的詞性不同。

順帶一提,C++20中volatile的許多用法都被廢棄了。

行為

結(jié)構(gòu)化綁定有三類行為,與上面的三種語(yǔ)法之間沒有對(duì)應(yīng)關(guān)系。

第一種情況,expression是數(shù)組,identifier-list的長(zhǎng)度必須與數(shù)組長(zhǎng)度相等。

第二種情況,對(duì)于expression的類型E,std::tuple_size是一個(gè)完整類型,則稱E為類元組(tuple-like)類型。在STL中,std::array、std::pairstd::tuple都是這樣的類型。此時(shí),identifier-list的長(zhǎng)度必須與std::tuple_size::value相等,每個(gè)標(biāo)識(shí)符的類型都通過std::tuple_element推導(dǎo)出(具體見后文),用成員get()get(e)初始化。顯然,這些標(biāo)準(zhǔn)庫(kù)設(shè)施是與語(yǔ)言核心綁定的。

第三種情況,E是非union類類型,綁定非靜態(tài)數(shù)據(jù)成員。所有非靜態(tài)數(shù)據(jù)成員都必須是public訪問屬性,全部在E中,或全部在E的一個(gè)基類中(即不能分散在多個(gè)類中)。identifier-list按照類中非靜態(tài)數(shù)據(jù)成員的聲明順序綁定,數(shù)量相等。

應(yīng)用

結(jié)構(gòu)化綁定擅長(zhǎng)處理純數(shù)據(jù)類型,包括自定義類型與std::tuple等,給實(shí)例的每一個(gè)字段分配一個(gè)變量名:

#include

structPoint
{
doublex,y;
};

Pointmidpoint(constPoint&p1,constPoint&p2)
{
return{(p1.x+p2.x)/2,(p1.y+p2.y)/2};
}

intmain()
{
Pointp1{1,2};
Pointp2{3,4};
auto[x,y]=midpoint(p1,p2);
std::cout<

配合其他語(yǔ)法糖,現(xiàn)代C++代碼可以很優(yōu)雅:

#include
#include

intmain()
{
std::mapmap;
if(auto&&[iter,inserted]=map.insert({1,2});inserted)
std::cout<

利用結(jié)構(gòu)化綁定在類元組類型上的行為,我們可以改變數(shù)據(jù)類型的結(jié)構(gòu)化綁定細(xì)節(jié),包括類型轉(zhuǎn)換、是否拷貝等:

#include
#include
#include

classTranscript{/*...*/};

classStudent
{
public:
constchar*name;
Transcriptscore;
std::stringgetName()const{returnname;}
constTranscript&getScore()const{returnscore;}
template
decltype(auto)get()const
{
ifconstexpr(I==0)
returngetName();
elseifconstexpr(I==1)
returngetScore();
else
static_assert(I
structtuple_size
:std::integral_constant{};

template<>
structtuple_element<0,?Student>{usingtype=decltype(std::declval().getName());};

template<>
structtuple_element<1,?Student>{usingtype=decltype(std::declval().getScore());};
}

intmain()
{
std::cout<

Student是一個(gè)數(shù)據(jù)類型,有兩個(gè)字段namescore。name是一個(gè)C風(fēng)格字符串,它大概是從C代碼繼承來(lái)的,我希望客戶能用上C++風(fēng)格的std::string;score屬于Transcript類型,表示學(xué)生的成績(jī)單,這個(gè)結(jié)構(gòu)比較大,我希望能傳遞const引用以避免不必要的拷貝。為此,我寫明了三要素:std::tuple_size、std::tuple_elementget。這種機(jī)制給了結(jié)構(gòu)化綁定很強(qiáng)的靈活性。

細(xì)節(jié)

#include
#include
#include

intmain()
{
std::pairpair{1,2.0};
intnumber=3;
std::tupletuple(number);
constauto&[i,f]=pair;
//i=4;//error
constauto&[ri]=tuple;
ri=5;
}

如果結(jié)構(gòu)化綁定i被聲明為const auto&,對(duì)應(yīng)的類型為int,那么它應(yīng)該是個(gè)const int&吧?i = 4;出錯(cuò)了,看起來(lái)正是如此。但是如何解釋ri = 5;是合法的呢?

這個(gè)問題需要系統(tǒng)地從頭談起。先引入一個(gè)名字e,E為其類型:

?當(dāng)expression是數(shù)組類型A,且ref-operator不存在時(shí),Ecv A,每個(gè)元素由expression中的對(duì)應(yīng)元素拷貝(= expression)或直接初始化({ expression }( expression );?否則,相當(dāng)于定義eattr cv-auto ref-operator e initializer;。

也就是說(shuō),方括號(hào)前面的修飾符都是作用于e的,而不是那些新聲明的變量。至于為什么第一條會(huì)獨(dú)立出來(lái),這是因?yàn)樵跇?biāo)準(zhǔn)C++中第二條的形式不能用于數(shù)組拷貝。

然后分三種情況討論:

?數(shù)組情形,ET的數(shù)組類型,則每個(gè)結(jié)構(gòu)化綁定都是指向e數(shù)組中元素的左值;被引類型(referenced type)為T;——結(jié)構(gòu)化綁定是左值,不是左值引用:int array[2]{ 1, 2 }; auto& [i, j] = array; static_assert(!std::is_reference_v);;?類元組情形,如果e是左值引用,則e是左值(lvalue),否則是消亡值(xvalue);記Tistd::tuple_element::type,則結(jié)構(gòu)化綁定vi的類型是Ti的引用;當(dāng)get返回左值引用時(shí)是左值引用,否則是右值引用;被引類型為Ti;——decltype對(duì)結(jié)構(gòu)化綁定有特殊處理,產(chǎn)生被引類型,在類元組情形下結(jié)構(gòu)化綁定的類型與被引類型是不同的;?數(shù)據(jù)成員情形,與數(shù)組類似,設(shè)數(shù)據(jù)成員mi被聲明為Ti類型,則結(jié)構(gòu)化綁定的類型是指向cv Ti的左值(同樣不是左值引用);被引類型為cv Ti。

至此,我想“結(jié)構(gòu)化綁定”的意義已經(jīng)明確了:標(biāo)識(shí)符總是綁定一個(gè)對(duì)象,該對(duì)象是另一個(gè)對(duì)象的成員(或數(shù)組元素),后者或是拷貝或是引用(引用不是對(duì)象,意會(huì)即可)。與引用類似,結(jié)構(gòu)化綁定都是既有對(duì)象的別名(這個(gè)對(duì)象可能是隱式的);與引用不同,結(jié)構(gòu)化綁定不一定是引用類型。

(不理解的話可以參考N465911.5節(jié),盡管你很可能會(huì)更加看不懂……)

現(xiàn)在可以解釋riconst的現(xiàn)象了:編譯器先創(chuàng)建了變量const auto& e = tuple;,Econst std::tuple&,std::tuple_element<0, E>::typeint&,std::get<0>(e)同樣返回int&,故riint&類型。

在面向底層的C++編程中常用union和位域(bit field),結(jié)構(gòu)化綁定支持這樣的數(shù)據(jù)成員。如果類有union類型成員,它必須是命名的,綁定的標(biāo)識(shí)符的類型為該union類型的左值;如果有未命名的union成員,則這個(gè)類不能用于結(jié)構(gòu)化綁定。

C++中不存在位域的指針和引用,但結(jié)構(gòu)化綁定可以是指向位域的左值:

#include

structBitField
{
intf1:4;
intf2:4;
intf3:4;
};

intmain()
{
BitFieldb{1,2,3};
auto&[f1,f2,f3]=b;
f2=4;
autoprint=[&]{std::cout<

程序輸出:

143
153

f2的功能就像位域的引用一樣,既能寫回原值,又不會(huì)超出位域的范圍。

還有一些語(yǔ)法細(xì)節(jié),比如get的名字查找、std::tuple_size沒有value、explicit拷貝構(gòu)造函數(shù)等,除非是深挖語(yǔ)法的language lawyer,在實(shí)際開發(fā)中不必糾結(jié)(上面這一堆已經(jīng)可以算language lawyer了吧)。

局限

以上代碼示例應(yīng)該已經(jīng)囊括了所有類型的結(jié)構(gòu)化綁定應(yīng)用,你能想象到的其他語(yǔ)法都是錯(cuò)的,包括但不限于:

?std::initializer_list初始化;因?yàn)?code style="white-space:pre-wrap;line-height:1.75;font-size:13.5px;color:rgb(221,17,68);background:rgba(27,31,35,.05);padding:3px 5px;">std::initializer_list的長(zhǎng)度是動(dòng)態(tài)的,但結(jié)構(gòu)化綁定的標(biāo)識(shí)符數(shù)量是靜態(tài)的。?用列表初始化——auto [x,y,z] = {1, "xyzzy"s, 3.14159};;這相當(dāng)于聲明了三個(gè)變量,但結(jié)構(gòu)化綁定的意圖在于綁定而非聲明。?不聲明而直接綁定——[iter, success] = mymap.insert(value);;這相當(dāng)于用std::tie,所以請(qǐng)繼續(xù)用std::tie。另外,由[開始可能與attributes混淆,給編譯器和編譯器設(shè)計(jì)者帶來(lái)壓力。?指明結(jié)構(gòu)化綁定的修飾符——auto [& x, const y, const& z] = f();;同樣是脫離了結(jié)構(gòu)化綁定的意圖。如果需要這樣的功能,或者一個(gè)個(gè)定義變量,或者手動(dòng)寫上三要素。?指明結(jié)構(gòu)化綁定的類型——SomeClass [x, y] = f();auto [x, std::string y] = f();;第一種可用auto [x, y] = SomeClass{ f() };代替;第二種同上一條。?顯式忽略一個(gè)結(jié)構(gòu)化綁定——auto [x, std::ignore, z] = f();;消除編譯器警告是一個(gè)理由,但是auto [x, y, z] = f(); (void)y;亦可。這還涉及一些語(yǔ)言問題,請(qǐng)移步P0144R23.8節(jié)。?標(biāo)識(shí)符嵌套——std::tuple, T4> f(); auto [ w, [x, y], z ] = f();;多寫一行吧。[同樣可能與attributes混淆。

以上語(yǔ)法都沒有納入C++20標(biāo)準(zhǔn),不過可能在將來(lái)成為C++語(yǔ)法的擴(kuò)展。

延伸

C++17的新特性不是孤立的,與結(jié)構(gòu)化綁定相關(guān)的有:

?類模板參數(shù)推導(dǎo)(class template argument deduction,CTAD,由構(gòu)造函數(shù)參數(shù)推導(dǎo)類模板參數(shù);?拷貝省略(copy elision),保證NRV(named return value)優(yōu)化;?constexprif,簡(jiǎn)化泛型代碼,消除部分SFINAE;?帶初始化的條件分支語(yǔ)句:語(yǔ)法糖,使代碼更加優(yōu)雅。


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4810

    瀏覽量

    68827
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1640

    瀏覽量

    49200
  • 結(jié)構(gòu)化
    +關(guān)注

    關(guān)注

    0

    文章

    27

    瀏覽量

    10326

原文標(biāo)題:聊聊結(jié)構(gòu)化綁定

文章出處:【微信號(hào):CPP開發(fā)者,微信公眾號(hào):CPP開發(fā)者】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    結(jié)構(gòu)化布線系統(tǒng)有哪些難題

      在布線系統(tǒng)中,結(jié)構(gòu)化布線也是非常重要的一環(huán),這里本文給大家主要講解了結(jié)構(gòu)化布線系統(tǒng)的規(guī)劃、安裝以及投資成本等問題,希望對(duì)您有所幫助?! ?b class='flag-5'>結(jié)構(gòu)化布線系統(tǒng)規(guī)劃  大多數(shù)電纜廠商為它們的產(chǎn)品規(guī)定了15
    發(fā)表于 05-19 13:46

    TrustZone結(jié)構(gòu)化消息是什么?

    大家好,我已閱讀任何與TrustZone相關(guān)的內(nèi)容,但我無(wú)法弄清楚這兩個(gè)世界是如何相互溝通的。我所能找到的只是TrustZone API規(guī)范中的內(nèi)容:客戶端和服務(wù)可以通過兩種機(jī)制進(jìn)行通信:結(jié)構(gòu)化
    發(fā)表于 03-20 08:58

    Deeplearningai結(jié)構(gòu)化機(jī)器學(xué)習(xí)項(xiàng)目

    Deeplearningai 結(jié)構(gòu)化機(jī)器學(xué)習(xí)項(xiàng)目 Week2 6-10
    發(fā)表于 05-18 15:12

    請(qǐng)問如何借助SC Express減少結(jié)構(gòu)化測(cè)試次數(shù)?

    如何借助SC Express減少結(jié)構(gòu)化測(cè)試次數(shù)?
    發(fā)表于 05-11 06:46

    怎么實(shí)現(xiàn)基于結(jié)構(gòu)化方法的無(wú)線傳感器網(wǎng)絡(luò)設(shè)計(jì)?

    怎么實(shí)現(xiàn)基于結(jié)構(gòu)化方法的無(wú)線傳感器網(wǎng)絡(luò)設(shè)計(jì)?
    發(fā)表于 05-31 06:34

    結(jié)構(gòu)化設(shè)計(jì)分為哪幾部分?結(jié)構(gòu)化設(shè)計(jì)的要求有哪些

    結(jié)構(gòu)化設(shè)計(jì)分為哪幾部分?結(jié)構(gòu)化設(shè)計(jì)的要求有哪些?結(jié)構(gòu)化設(shè)計(jì)主要包括哪些部分?
    發(fā)表于 12-23 06:15

    ISSP結(jié)構(gòu)化ASIC解決方案

    ISSP結(jié)構(gòu)化ASIC解決方案 結(jié)構(gòu)化專用集成電路(structured ASIC)對(duì)設(shè)計(jì)工程師而言還是一個(gè)新名詞,然而目前已經(jīng)有多家公司正計(jì)劃涉足這一領(lǐng)域??焖俟?/div>
    發(fā)表于 12-27 13:32 ?1263次閱讀
    ISSP<b class='flag-5'>結(jié)構(gòu)化</b>ASIC解決方案

    結(jié)構(gòu)化布線的綜合說(shuō)明

    結(jié)構(gòu)化布線的綜合說(shuō)明 一、結(jié)構(gòu)化布線系統(tǒng)簡(jiǎn)介     隨著計(jì)算機(jī)和通信技術(shù)的飛速發(fā)展,網(wǎng)絡(luò)應(yīng)用
    發(fā)表于 04-14 17:16 ?748次閱讀

    什么叫結(jié)構(gòu)化的算法_算法和結(jié)構(gòu)化數(shù)據(jù)初識(shí)

    結(jié)構(gòu)化算法是由一些基本結(jié)構(gòu)順序組成的,就是把一個(gè)大的功能的實(shí)現(xiàn)分隔為許多個(gè)小功能的實(shí)現(xiàn)。在基本結(jié)構(gòu)之間不存在向前或向后的跳轉(zhuǎn),流程的轉(zhuǎn)移只存在于一個(gè)基本的結(jié)構(gòu)范圍內(nèi)。一個(gè)非
    發(fā)表于 01-03 16:09 ?1.2w次閱讀
    什么叫<b class='flag-5'>結(jié)構(gòu)化</b>的算法_算法和<b class='flag-5'>結(jié)構(gòu)化</b>數(shù)據(jù)初識(shí)

    結(jié)構(gòu)化布線系統(tǒng)的四點(diǎn)注意事項(xiàng)

    布線系統(tǒng)結(jié)構(gòu)化 結(jié)構(gòu)化布線 title=結(jié)構(gòu)化布線結(jié)構(gòu)化布線 title=結(jié)構(gòu)化布線結(jié)構(gòu)化布線系
    發(fā)表于 10-16 10:52 ?1249次閱讀

    安防監(jiān)控視頻結(jié)構(gòu)化那些事兒

    即便不考慮各個(gè)監(jiān)控系統(tǒng)之間的信息關(guān)聯(lián),光瀏覽這些視頻就需要花費(fèi)大量的人力物力。解決這一問題的核心技術(shù)即視頻結(jié)構(gòu)化描述技術(shù),將海量視頻或圖片的非結(jié)構(gòu)化數(shù)據(jù)提取并轉(zhuǎn)化為結(jié)構(gòu)化信息描述。
    的頭像 發(fā)表于 03-20 10:20 ?3293次閱讀

    FPGA模塊設(shè)計(jì)與AlteraHardCopy結(jié)構(gòu)化ASIC

    本文檔的主要內(nèi)容詳細(xì)介紹的是FPGA模塊設(shè)計(jì)與AlteraHardCopy結(jié)構(gòu)化ASIC。
    發(fā)表于 01-20 17:03 ?6次下載
    FPGA模塊<b class='flag-5'>化</b>設(shè)計(jì)與AlteraHardCopy<b class='flag-5'>結(jié)構(gòu)化</b>ASIC

    CFD 設(shè)計(jì)利器:結(jié)構(gòu)化和非結(jié)構(gòu)化網(wǎng)格的組合使用

    在CFD的發(fā)展歷史中,結(jié)構(gòu)化網(wǎng)格出現(xiàn)最早,至今仍在使用。結(jié)構(gòu)化網(wǎng)格有幾個(gè)主要優(yōu)點(diǎn),如精度高、生成速度快、單元分布均勻。有些工具擅長(zhǎng)繪制這類網(wǎng)格,例如CadenceFidelityAutomesh
    的頭像 發(fā)表于 12-23 08:12 ?2073次閱讀
    CFD 設(shè)計(jì)利器:<b class='flag-5'>結(jié)構(gòu)化</b>和非<b class='flag-5'>結(jié)構(gòu)化</b>網(wǎng)格的組合使用

    結(jié)構(gòu)化布線的好處多嗎

    結(jié)構(gòu)化布線是網(wǎng)絡(luò)系統(tǒng)中的重要組成部分,因?yàn)樗鼮閿?shù)據(jù)傳輸提供了強(qiáng)大、可擴(kuò)展且可靠的基礎(chǔ)。通過遵守全球公認(rèn)的標(biāo)準(zhǔn),結(jié)構(gòu)化布線可促進(jìn)高速連接、簡(jiǎn)化故障排除并確保未來(lái)的可擴(kuò)展性。考慮到這些優(yōu)勢(shì),企業(yè)應(yīng)優(yōu)先
    的頭像 發(fā)表于 04-07 11:15 ?470次閱讀

    什么是結(jié)構(gòu)化網(wǎng)絡(luò)布線?結(jié)構(gòu)化網(wǎng)絡(luò)布線有哪些好處?

    在電纜領(lǐng)域,結(jié)構(gòu)化網(wǎng)絡(luò)布線這個(gè)術(shù)語(yǔ)經(jīng)常被提及。人們將其用作流行語(yǔ),但它的真正含義是什么?結(jié)構(gòu)化布線到底是什么? 為了了解真正的含義,讓我們看它的一些相關(guān)定義。 根據(jù)光纖協(xié)會(huì)的說(shuō)法,結(jié)構(gòu)化布線是由
    的頭像 發(fā)表于 04-11 11:54 ?550次閱讀