遨游于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*?類型?
接下來我們將利用c++11的可變參數(shù)模板(variadic templates)拾徙、參數(shù)包(parameters pack)洲炊、完美轉(zhuǎn)發(fā)(perfect forwarding)等特性來實(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)的返回跟束。接下來我們要做模板的偏特化,對(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ā)生拷貝箩帚。
如果有想學(xué)習(xí)c++的程序員真友,可來我們的C/C++學(xué)習(xí)扣qun:589348389,
免費(fèi)送C++的視頻教程噢紧帕!
我每晚上8點(diǎn)還會(huì)在群內(nèi)直播講解C/C++知識(shí)盔然,歡迎大家前來學(xué)習(xí)哦。