最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關(guān)于C++菱形運(yùn)算符深度解析

 更新時(shí)間:2024年04月30日 09:07:13   作者:apocelipes  
從語言標(biāo)準(zhǔn)來說,c++里沒有什么菱形運(yùn)算符,c++20里雖然新增了一個(gè)運(yùn)算符operator<=>,但這個(gè)和所謂的菱形運(yùn)算符沒有任何關(guān)系,下面通過本文探討C++里也有菱形運(yùn)算符嗎這一問題探討,感興趣的朋友跟隨小編一起看看吧

最近在翻《c++函數(shù)式編程》的時(shí)候看到有一小節(jié)在說c++14新增了“菱形運(yùn)算符”。我尋思c++里好像沒什么運(yùn)算符叫這名字啊,而且c++14新增的功能很少,我也不記得有添加這種語法特性。一瞬間我有些懷疑我的記憶了,所以為了查漏補(bǔ)缺,我寫了這篇文章。

什么是菱形運(yùn)算符

這個(gè)概念在Java里比較多見:

List<String> myList = new ArrayList<>();

這東西在Java里的學(xué)名是diamond operator,表示使用泛型類并且類型參數(shù)在左側(cè)的表達(dá)式已給出因此在右側(cè)可以省略。

簡單的說就是讓你少寫幾次重復(fù)的類型參數(shù)。因?yàn)榭雌饋硐駛€(gè)菱形所以得名菱形運(yùn)算符。

然后我們偶爾會(huì)在c++里看到形狀上很相似的東西:

std::sort(vec.begin(), vec.end(), std::greater<>());

<>出現(xiàn)在模板的特化中是我們所熟悉的,但這個(gè)std::greater<>()是什么呢?

c++沒有菱形運(yùn)算符

先說結(jié)論,從語言標(biāo)準(zhǔn)來說,c++里沒有什么菱形運(yùn)算符。

c++20里雖然新增了一個(gè)運(yùn)算符operator<=>,但這個(gè)和所謂的菱形運(yùn)算符沒有任何關(guān)系。

那問題來了,std::greater<>()是什么以及為什么書里說是c++14新增的特性呢?難道書里瞎說的嗎?但事實(shí)是這樣的示例代碼在c++14以及之后的標(biāo)準(zhǔn)下可以正常編譯運(yùn)行,而且這本書的質(zhì)量尚可,雖然會(huì)在措辭上犯些小錯(cuò)(比如c++沒有菱形運(yùn)算符)但不至于花大篇幅去胡說八道。

當(dāng)然,要想回答這個(gè)問題我們得先復(fù)習(xí)點(diǎn)基礎(chǔ)知識(shí)。

<>在c++里的作用

先說結(jié)論,在c++里看到<>,絕大多數(shù)都是在為模板提供類型參數(shù),當(dāng)然這種東西我們不討論:(a<1, 2>b),這里<>是在兩個(gè)不同的表達(dá)式里。

那既然用來提供類型參數(shù),那為什么可以啥都不提供呢?答案是有兩類情況確實(shí)可以。

第一類是在函數(shù)模板上,類型參數(shù)可以自動(dòng)推導(dǎo)時(shí):

template <typename T>
void f(const T&)
{
    std::cout << "f<T>\n";
}
template <>
void f(const int&)
{
    std::cout << "f<int>\n";
}
void f(const int&)
{
    std::cout << "f\n";
}
int main()
{
    f(1);    // f
    f<>(1);  // f<int>
    f(1.2);  // f<T>
}

非模板函數(shù)在重載決議中的優(yōu)先級(jí)總是高于模板的,因此f(1)這樣的表達(dá)式總是會(huì)用到最下面定義的那個(gè)非模板函數(shù)f。這時(shí)候我們可以用f<int>(1)來直接調(diào)用函數(shù)模板f,而函數(shù)模板的類型參數(shù)如果能從參數(shù)推導(dǎo)出來的話,可以不明確給出(也就是后面的f(1.2)那樣的),而在我們現(xiàn)在這句表達(dá)式里,我們既要明確使用函數(shù)模板,又想讓類型參數(shù)被自動(dòng)推導(dǎo),就得使用f<>(1)

另一種情況不分類模板還是函數(shù)模板,當(dāng)模板的類型參數(shù)有默認(rèn)值時(shí),可以靠<>來使用這些默認(rèn)值:

template <typename T = void>
struct Wrapper
{
    using wrappered = T;
};
// Wrapper<> 等于 Wrapper<void>
static_assert(std::is_same_v<Wrapper<>::wrappered, Wrapper<void>::wrappered>);

在第二種情況下,因?yàn)闆]顯示給出類型參數(shù),且這里沒法使用類型推導(dǎo),因此編譯器使用了類型參數(shù)的默認(rèn)值,這里是void。

觀察比較仔細(xì)的話其實(shí)會(huì)發(fā)現(xiàn)上面兩種情況其實(shí)是一件事,<>相當(dāng)于沒有顯示給出任何類型參數(shù),于是對(duì)這些沒有顯示指定的類型參數(shù),編譯器會(huì)先嘗試類型推導(dǎo),如果沒法推導(dǎo)則會(huì)檢查這些類型參數(shù)是否有默認(rèn)值,有就利用默認(rèn)值。如果上面這兩步都沒法得到能正常使用的類型參數(shù),模板會(huì)被SFINAE淘汰或者報(bào)出編譯錯(cuò)誤。

這并不是什么新語法,是從有模板開始就一直存在的規(guī)則。

現(xiàn)在我們可以看看std::greater<>()是什么了,首先std::greater是個(gè)類模板,然后它接受一個(gè)類型參數(shù),這個(gè)參數(shù)在c++14之后有了默認(rèn)值void,因此std::greater<>()std::greater<void>()。

c++14中究竟添加了什么

既然c++14并沒有添加“菱形運(yùn)算符”,那究竟新增了什么呢?

在已經(jīng)知道了std::greater<>()的真身后,找起來就很容易了,所以我很快找到了對(duì)應(yīng)的新特性:n3421

這個(gè)特性是這樣的:原先我們要用標(biāo)準(zhǔn)庫提供的謂詞模板,需要自己指明參數(shù)類型,這樣寫起來很麻煩而且對(duì)于那種嵌套的或者元素類型復(fù)雜的容器來說寫明參數(shù)類型不僅費(fèi)時(shí)而且費(fèi)力,更要命的是對(duì)于map,一不小心是會(huì)有性能問題的:

for_each(map.begin(), map.end(), std::pred<std::pair<std::string, int64_t>>());

上述代碼的問題在于正確的參數(shù)類型應(yīng)該是std::pair<const std::string, int64_t>,我們漏掉了const,這會(huì)導(dǎo)致pair整個(gè)被復(fù)制一遍,性能是無比底下的。要徹底避免這種錯(cuò)誤,就得利用自動(dòng)類型推導(dǎo)。

然而前面說了,標(biāo)準(zhǔn)庫提供的謂詞基本全是類模板,類模板的模板參數(shù)要么依賴默認(rèn)值要么得顯示指定,怎么才能依賴自動(dòng)推導(dǎo)呢。

于是這個(gè)新特性最精彩的地方來了:原先的模板的調(diào)用運(yùn)算符不是模板參數(shù)也是定死的,但我們可以新加一個(gè)默認(rèn)參數(shù),然后針對(duì)這個(gè)默認(rèn)參數(shù)的類型進(jìn)行完全特化,在特化里提供一個(gè)泛型的operator(),這樣就能利用函數(shù)模板來自動(dòng)推導(dǎo)參數(shù)類型了,而且以前的代碼不受影響。

默認(rèn)參數(shù)的設(shè)置也是有講究的,需要用一個(gè)謂詞用不到的且不會(huì)影響老代碼的類型,運(yùn)氣不錯(cuò),void正好符合條件(void上幾乎沒法做什么操作,因此也不會(huì)被指定給這些謂詞做類型參數(shù)),因此現(xiàn)在的greater的代碼是下面這樣的:

// 注意默認(rèn)值是void
template <typename T = void> struct greater {
    constexpr bool operator()(const T& lhs, const T& rhs) const 
    {
        return lhs > rhs;
    }
};
// 針對(duì)greater<void>的完全特化
template <> struct greater<void> {
    template <class T, class U> auto operator()(T&& t, U&& u) const
    -> decltype(std::forward<T>(t) > std::forward<U>(u))
    { return std::forward<T>(t) > std::forward<U>(u); }
};

當(dāng)使用std::greater<T>()的時(shí)候,代碼的邏輯和原來一樣,當(dāng)使用std::greater<void>()的時(shí)候,返回的Functor的函數(shù)調(diào)用運(yùn)算符是個(gè)模板,可以自己推導(dǎo)參數(shù)類型和返回值類型。至于為啥greater<void>的內(nèi)部構(gòu)造可以和其他情況實(shí)例化的greater區(qū)別這么大,這個(gè)是c++的特性:模板的不同實(shí)例之間是可以異構(gòu)的。

而且因?yàn)轭愋蛥?shù)的默認(rèn)值就是void,因此可以簡寫成std::greater<>()。

所以c++14只是給標(biāo)準(zhǔn)庫里可以代替運(yùn)算符的模板們?cè)黾恿四J(rèn)類型參數(shù)和一個(gè)泛型的調(diào)用運(yùn)算符,利用這些可以簡化代碼并確保類型安全。

真相是其實(shí)沒啥菱形運(yùn)算符,只是利用了以前就存在的模板的特性簡化了標(biāo)準(zhǔn)庫的使用,讓人少寫點(diǎn)字。達(dá)成的效果倒是和Java的菱形運(yùn)算符差不多。

總結(jié)

顯然書里有夸大成分,老話說盡信書不如無書,還得小心檢驗(yàn)才是。

順便我們復(fù)習(xí)了現(xiàn)代c++的重要原則:能依賴自動(dòng)類型推導(dǎo)的地方,沒必要自己手寫。

因此應(yīng)該多寫這樣的代碼:std::sort(vec.begin(), vec.end(), std::greater<>());

不過還有最后一個(gè)問題,為啥不直接用lambda呢?那是因?yàn)槟苤付愋蛥?shù)的泛型lambda要在c++20才出現(xiàn),在這之前想要讓lambda完全做到類型安全得費(fèi)點(diǎn)功夫,而且lambda整體上也不如直接用標(biāo)準(zhǔn)庫提供的std::greater<>()、std::less<>()之類的簡潔易懂。

到此這篇關(guān)于C++里的菱形運(yùn)算符的文章就介紹到這了,更多相關(guān)C++菱形運(yùn)算符內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • c++難以發(fā)現(xiàn)的bug(有趣)

    c++難以發(fā)現(xiàn)的bug(有趣)

    這篇文章主要介紹了c++難以發(fā)現(xiàn)的bug(有趣)的相關(guān)資料,需要的朋友可以參考下
    2017-10-10
  • C++中vector與remove()函數(shù)詳解

    C++中vector與remove()函數(shù)詳解

    這篇文章主要介紹了C++中vector與remove()函數(shù),本文實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • VC實(shí)現(xiàn)動(dòng)態(tài)菜單的創(chuàng)建方法

    VC實(shí)現(xiàn)動(dòng)態(tài)菜單的創(chuàng)建方法

    這篇文章主要介紹了VC實(shí)現(xiàn)動(dòng)態(tài)菜單的創(chuàng)建方法,需要的朋友可以參考下
    2014-07-07
  • C語言動(dòng)態(tài)內(nèi)存分配圖文講解

    C語言動(dòng)態(tài)內(nèi)存分配圖文講解

    給數(shù)組分配多大的空間?你是否和初學(xué)C時(shí)的我一樣,有過這樣的疑問。這一期就來聊一聊動(dòng)態(tài)內(nèi)存的分配,讀完這篇文章,你可能對(duì)內(nèi)存的分配有一個(gè)更好的理解
    2023-01-01
  • C++回溯算法中子集問題分析探討

    C++回溯算法中子集問題分析探討

    回溯法是一種選優(yōu)搜索法,按選優(yōu)條件向前搜索,以達(dá)到目標(biāo)。但當(dāng)探索到某一步時(shí),發(fā)現(xiàn)原先選擇并不優(yōu)或達(dá)不到目標(biāo),就退回一步重新選擇,這種走不通就退回再走的技術(shù)為回溯法,而滿足回溯條件的某個(gè)狀態(tài)的點(diǎn)稱為回溯點(diǎn)
    2023-03-03
  • C++ Boost Archive超詳細(xì)講解

    C++ Boost Archive超詳細(xì)講解

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個(gè)可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱
    2022-12-12
  • C++ Boost PropertyTree示例超詳細(xì)講解

    C++ Boost PropertyTree示例超詳細(xì)講解

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個(gè)可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱
    2022-11-11
  • C++中兩種字符串定義方式和區(qū)別介紹

    C++中兩種字符串定義方式和區(qū)別介紹

    大家好,本篇文章主要講的是C++中兩種字符串定義方式和區(qū)別介紹,感興趣的同學(xué)趕快來看一看吧,對(duì)你有幫助的話記得收藏一下
    2022-01-01
  • 基于C語言編寫一個(gè)簡單的Web服務(wù)器

    基于C語言編寫一個(gè)簡單的Web服務(wù)器

    C語言可以干大事,這篇文章主要為大家詳細(xì)介紹了如何基于C語言可以完成一個(gè)簡易的Web服務(wù)器,希望這篇文章會(huì)幫你你對(duì)C語言有更深入的理解
    2024-03-03
  • 新手向超詳細(xì)的C語言實(shí)現(xiàn)動(dòng)態(tài)順序表

    新手向超詳細(xì)的C語言實(shí)現(xiàn)動(dòng)態(tài)順序表

    本文主要介紹了C語言實(shí)現(xiàn)動(dòng)態(tài)順序表,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評(píng)論

鸡东县| 鄯善县| 芒康县| 博兴县| 河池市| 台江县| 巴青县| 正蓝旗| 固原市| 临泽县| 衡南县| 轮台县| 宜章县| 乌拉特中旗| 忻城县| 鸡泽县| 洱源县| 遂昌县| 体育| 鹤壁市| 绥化市| 宁津县| 孝感市| 绩溪县| 通江县| 二连浩特市| 清原| 青田县| 曲水县| 华蓥市| 策勒县| 涞水县| 商洛市| 静安区| 轮台县| 永宁县| 西安市| 久治县| 台湾省| 勐海县| 湘阴县|