遨游于C++世界時(shí)矛纹,最討厭的當(dāng)屬于對(duì)c-style的兼容 。
在格式化字符串時(shí)光稼,通常使用的是?snprintf?這個(gè)c函數(shù)或南。?snprintf?是?sprintf?的安全版,能夠避免緩沖區(qū)溢出钟哥。
charbuf[1024] = {0};std::strings ="Hello world";snprintf(buf,sizeof(buf),"format str: %s", s.c_str());
snprintf?接受的參數(shù)跟?printf?差不多,都是c-style的數(shù)據(jù)類型瞎访,如?%s?接受的是?const char*?類型的數(shù)據(jù)腻贰,這就需要我們將?std::string?做一個(gè)轉(zhuǎn)換。這一個(gè)小小的轉(zhuǎn)換讓我覺得非常不爽扒秸,有沒有可能讓?std::string?在做某個(gè)函數(shù)的參數(shù)時(shí)能自動(dòng)做轉(zhuǎn)換播演?甚至讓一個(gè)將一個(gè)普通的對(duì)象自動(dòng)轉(zhuǎn)換成?const char*?類型?
接下來(lái)我們將利用c++11的可變參數(shù)模板(variadic templates)伴奥、參數(shù)包(parameters pack)写烤、完美轉(zhuǎn)發(fā)(perfect forwarding)等特性來(lái)實(shí)現(xiàn)這一想法。
參數(shù)列表完美轉(zhuǎn)發(fā)
寫C++代碼時(shí)拾徙,我滿腦子都是怎么最大限度地提高性能洲炊。我們這次的目標(biāo)也一樣,在提供方便的同時(shí)尼啡,不要對(duì)性能有太大影響暂衡,甚至不影響。
首先是要將傳入?fmt?函數(shù)的參數(shù)完美轉(zhuǎn)發(fā)至?snprintf?崖瞭。
templatestringfmt(constchar*format, Args&&... args){charbuf[128] = {0};snprintf(buf,sizeof(buf), format, convert(std::forward(args))...);returnbuf;}
這是一個(gè)可變參數(shù)模板狂巢,args表示傳入的參數(shù)們。我們的思路是將傳入的參數(shù)做一個(gè)轉(zhuǎn)換之后傳給?snprintf?书聚。
為了原封不動(dòng)的保持左右值引用唧领,首先是用?Args&&?代替?Args?的參數(shù)類型,此處模板函數(shù)的Args需要編譯器推導(dǎo)雌续,所以是一個(gè)通用引用(Universal reference)斩个,可以指代左值或右值。用?std::forward<Args>?能保持參數(shù)的左右值性質(zhì)驯杜,做到參數(shù)的完美轉(zhuǎn)發(fā)萨驶。
自動(dòng)參數(shù)轉(zhuǎn)換
在?convert?這個(gè)函數(shù)中,我們要將特定的類型轉(zhuǎn)換成?const char*?類型艇肴,而那些能被?snprintf?接受的類型如?int?,?double?,?char*?腔呜,則原封不動(dòng)的返回叁温。
convert?函數(shù)針對(duì)不同的參數(shù)類型需要返回不同的類型。這里也將返回值作為一個(gè)模板類型即可核畴。
templatestructitem_return {usingtype = T&&;};
convert?函數(shù)的定義為:
templateinlinetypenameitem_return::typeconvert(T&& arg){returnstatic_cast(arg);}
convert函數(shù)默認(rèn)將傳入的參數(shù)原封不動(dòng)的返回膝但。接下來(lái)我們要做模板的偏特化,對(duì)于指定的對(duì)象谤草,將其轉(zhuǎn)換為const char *類型
// lvaluetemplate<>structitem_return {usingtype =constchar*;};template<>inlinetypenameitem_return::type convert(obj &arg) {std::cout<<"receive lvalue\n";returnarg.s.c_str();}// rvaluetemplate<>structitem_return {usingtype =constchar*;};template<>inlinetypenameitem_return::type convert(obj &&arg) {std::cout<<"receive rvalue\n";returnarg.s.c_str();}
注意跟束,返回值也是需要偏特化的。
最后
我構(gòu)造了一個(gè)class丑孩,hook他的兩個(gè)構(gòu)造函數(shù)以便于觀察是否發(fā)生了拷貝冀宴。
classobj {public:strings;? obj(constchar* ss) {? ? s = ss;? }? obj(constobj& other):s(other.s) {printf("copy constructor\n");? }? obj(obj&& other):s(other.s) {printf("move constructor\n");? ? other.s.clear();? }};
之后我們使用fmt函數(shù),就能像格式化c-style字符串一樣温学,格式化任意一個(gè)對(duì)象啦略贮。
intmain(){obja("haha");intb =3;std::cout<< fmt("%s %s\n%d %d", a, obj("xixi"), b,2) <
運(yùn)行結(jié)果為
receivelvaluereceivervaluehaha xixi32
很好,并沒有發(fā)生拷貝仗岖。
看我主頁(yè)簡(jiǎn)介免費(fèi)C++學(xué)習(xí)資源逃延,視頻教程、職業(yè)規(guī)劃轧拄、面試詳解揽祥、學(xué)習(xí)路線、開發(fā)工具
每晚8點(diǎn)直播講解C++編程技術(shù)檩电。