久久久久久久av_日韩在线中文_看一级毛片视频_日本精品二区_成人深夜福利视频_武道仙尊动漫在线观看

C++17 中新的基于范圍的 for 循環(huán)如何幫助 Ranges

How the new range-based for loop in C++17 helps Ranges TS?(C++17 中新的基于范圍的 for 循環(huán)如何幫助 Ranges TS?)
本文介紹了C++17 中新的基于范圍的 for 循環(huán)如何幫助 Ranges TS?的處理方法,對(duì)大家解決問(wèn)題具有一定的參考價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧!

問(wèn)題描述

委員會(huì)改變了基于范圍的 for 循環(huán):

  • C++11:

    <代碼>{汽車(chē)&&__range = range_expression ;for (auto __begin = begin_expr, __end = end_expr;__開(kāi)始!= __結(jié)束;++__開(kāi)始){range_declaration = *__begin;循環(huán)語(yǔ)句}}

  • 到 C++17:

    <代碼>{汽車(chē)&&__range = range_expression ;自動(dòng) __begin = begin_expr ;自動(dòng) __end = end_expr ;for ( ; __begin != __end; ++__begin) {range_declaration = *__begin;循環(huán)語(yǔ)句}}

人們說(shuō)這將使實(shí)施 Ranges TS 更容易.你能給我舉一些例子嗎?

解決方案

C++11/14 range-for was overconstrained...

WG21 論文是 P0184R0 其動(dòng)機(jī)如下:

<塊引用>

現(xiàn)有的基于范圍的 for 循環(huán)受到過(guò)度約束.結(jié)束迭代器永遠(yuǎn)不會(huì)遞增、遞減或取消引用.需要它作為一個(gè)迭代器沒(méi)有實(shí)際用途.

從您發(fā)布的 Standardese 中可以看出,范圍的 end 迭代器僅用于循環(huán)條件 __begin != __end;.因此,end 只需要與 begin 相等,不需要可解引用或可遞增.

...它扭曲了分隔迭代器的 operator==.

那么這有什么缺點(diǎn)呢?好吧,如果您有一個(gè)標(biāo)記分隔的范圍(C 字符串、文本行等),那么您必須將循環(huán)條件硬塞到迭代器的 operator== 中,本質(zhì)上是這樣

#include 模板 結(jié)構(gòu)體字符串迭代器{char const* ptr = nullptr;朋友自動(dòng)運(yùn)算符==(StringIterator lhs,StringIterator rhs){返回 lhs.ptr ?(rhs.ptr || (*lhs.ptr == Delim)) : (!rhs.ptr || (*rhs.ptr == Delim));}朋友自動(dòng)運(yùn)算符!=(StringIterator lhs,StringIterator rhs){返回 !(lhs == rhs);}自動(dòng)&運(yùn)算符*() { 返回 *ptr;}自動(dòng)&運(yùn)算符++(){++ptr;返回 *this;}};模板 類(lèi) StringRange{StringIterator它;上市:StringRange(char const* ptr) : it{ptr} {}自動(dòng)開(kāi)始(){ 返回它;}auto end() { return StringIterator{};}};int main(){//Hello World",沒(méi)有感嘆號(hào)for (auto const& c : StringRange<'!'>{"Hello World!"})std::cout <<C;}

實(shí)時(shí)示例 使用 g++ -std=c++14,(組裝使用 gcc.godbolt.org)

上述 StringIterator<>operator== 在其參數(shù)上是對(duì)稱(chēng)的,并且不依賴(lài)于 range-for 是否為 begin != endend != begin (否則你可以作弊并將代碼切成兩半).

對(duì)于簡(jiǎn)單的迭代模式,編譯器能夠優(yōu)化operator==內(nèi)部的復(fù)雜邏輯.事實(shí)上,對(duì)于上面的例子,operator== 被簡(jiǎn)化為一個(gè)單一的比較.但這會(huì)繼續(xù)適用于范圍和過(guò)濾器的長(zhǎng)管道嗎?誰(shuí)知道.很可能需要英雄般的優(yōu)化級(jí)別.

C++17 將放寬約束,這將簡(jiǎn)化分隔范圍...

那么簡(jiǎn)化究竟體現(xiàn)在什么地方呢?在 operator== 中,現(xiàn)在有額外的重載采用迭代器/哨兵對(duì)(在兩個(gè)順序中,為了對(duì)稱(chēng)).所以運(yùn)行時(shí)邏輯變成了編譯時(shí)邏輯.

#include 模板 struct StringSentinel {};結(jié)構(gòu)體字符串迭代器{char const* ptr = nullptr;模板 <char Delim>朋友自動(dòng)操作符==(StringIterator lhs, StringSentinel rhs) {返回 *lhs.ptr == Delim;}模板 <char Delim>朋友自動(dòng)操作符==(StringSentinel lhs, StringIterator rhs) {返回rhs == lhs;}模板 <char Delim>朋友自動(dòng)運(yùn)算符!=(StringIterator lhs,StringSentinel rhs){返回 !(lhs == rhs);}模板 <char Delim>朋友自動(dòng)運(yùn)算符!=(StringSentinel lhs, StringIterator rhs){返回 !(lhs == rhs);}自動(dòng)&運(yùn)算符*() { 返回 *ptr;}自動(dòng)&運(yùn)算符++(){++ptr;返回 *this;}};模板 類(lèi) StringRange{StringIterator 吧;上市:StringRange(char const* ptr) : it{ptr} {}自動(dòng)開(kāi)始(){ 返回它;}auto end() { return StringSentinel{};}};int main(){//Hello World",沒(méi)有感嘆號(hào)for (auto const& c : StringRange<'!'>{"Hello World!"})std::cout <<C;}

現(xiàn)場(chǎng)示例 使用 g++ -std=c++1z(組裝,使用 gcc.godbolt.org,幾乎與上一個(gè)例子).

...實(shí)際上將支持完全通用的原始D 風(fēng)格"范圍.

WG21 論文 N4382 有以下建議:

<塊引用>

C.6 Range Facade 和適配器實(shí)用程序 [future.facade]

1 直到它用戶(hù)創(chuàng)建自己的迭代器類(lèi)型變得微不足道,完整的迭代器的潛力將仍未實(shí)現(xiàn).范圍抽象使之成為可能.使用正確的庫(kù)組件,它應(yīng)該是用戶(hù)可以用最少的界面定義一個(gè)范圍(例如,currentdonenext 成員),并具有迭代器類(lèi)型自動(dòng)生成.這樣的范圍外觀類(lèi)模板保留為未來(lái)的工作.

本質(zhì)上,這等于 D 風(fēng)格的范圍(這些原語(yǔ)被稱(chēng)為 emptyfrontpopFront).只有這些原語(yǔ)的分隔字符串范圍看起來(lái)像這樣:

template 類(lèi) PrimitiveStringRange{字符常量* ptr;上市:PrimitiveStringRange(char const* c) : ptr{c} {}自動(dòng)&當(dāng)前(){ 返回 *ptr;}auto done() const { return *ptr == Delim;}自動(dòng)下一個(gè)(){++ptr;}};

如果不知道原始范圍的底層表示,如何從中提取迭代器?如何將其調(diào)整為可與 range-for 一起使用的范圍?這是一種方法(另請(qǐng)參閱@EricNiebler 的系列博文) 和@TC 的評(píng)論:

#include //在迭代器/哨兵對(duì)的開(kāi)始/結(jié)束對(duì)當(dāng)前/完成/旁邊調(diào)整任何原始范圍模板<派生類(lèi)>struct RangeAdaptor : 私有派生{使用派生::派生;結(jié)構(gòu)哨兵{};結(jié)構(gòu)體迭代器{派生* rng;朋友自動(dòng)操作符==(Iterator it, Sentinel) { return it.rng->done();}朋友自動(dòng)運(yùn)算符==(Sentinel,Iterator it){ return it.rng->done();}朋友自動(dòng)運(yùn)算符!=(迭代器lhs,哨兵rhs){返回!(lhs==rhs);}朋友自動(dòng)運(yùn)算符!=(哨兵lhs,迭代器rhs){返回!(lhs==rhs);}自動(dòng)&運(yùn)算符*(){返回rng->當(dāng)前();}自動(dòng)&運(yùn)算符++(){rng->next();返回 *this;}};自動(dòng)開(kāi)始(){返回迭代器{this};}自動(dòng)結(jié)束(){返回哨兵{};}};int main(){//Hello World",沒(méi)有感嘆號(hào)for (auto const& c : RangeAdaptor<PrimitiveStringRange<'!'>>{"Hello World!"})std::cout <<C;}

現(xiàn)場(chǎng)示例 使用 g++ -std=c++1z(組裝使用 gcc.godbolt.org)

結(jié)論:哨兵不僅是一種將分隔符壓入類(lèi)型系統(tǒng)的可愛(ài)機(jī)制,它們還足夠通用支持原始的D 風(fēng)格"范圍(它們本身可能沒(méi)有迭代器的概念)作為新的 C++1z range-for 的零開(kāi)銷(xiāo)抽象.>

The committee changed the range-based for loop from:

  • C++11:

    {
       auto && __range = range_expression ; 
       for (auto __begin = begin_expr, __end = end_expr; 
           __begin != __end; ++__begin) { 
           range_declaration = *__begin; 
           loop_statement 
       }
    } 
    

  • to C++17 :

    {        
        auto && __range = range_expression ; 
        auto __begin = begin_expr ;
        auto __end = end_expr ;
        for ( ; __begin != __end; ++__begin) { 
            range_declaration = *__begin; 
            loop_statement 
        } 
    }
    

And people said that this will make implementing Ranges TS easier. Can you give me some examples?

解決方案

C++11/14 range-for was overconstrained...

The WG21 paper for this is P0184R0 which has the following motivation:

The existing range-based for loop is over-constrained. The end iterator is never incremented, decremented, or dereferenced. Requiring it to be an iterator serves no practical purpose.

As you can see from the Standardese that you posted, the end iterator of a range is only used in the loop-condition __begin != __end;. Hence end only needs to be equality comparable to begin, and it does not need to be dereferenceable or incrementable.

...which distorts operator== for delimited iterators.

So what disadvantage does this have? Well, if you have a sentinel-delimited range (C-string, line of text, etc.), then you have to shoehorn the loop-condition into the iterator's operator==, essentially like this

#include <iostream>

template <char Delim = 0>
struct StringIterator
{
    char const* ptr = nullptr;   

    friend auto operator==(StringIterator lhs, StringIterator rhs) {
        return lhs.ptr ? (rhs.ptr || (*lhs.ptr == Delim)) : (!rhs.ptr || (*rhs.ptr == Delim));
    }

    friend auto operator!=(StringIterator lhs, StringIterator rhs) {
        return !(lhs == rhs);
    }

    auto& operator*()  {        return *ptr;  }
    auto& operator++() { ++ptr; return *this; }
};

template <char Delim = 0>
class StringRange
{
    StringIterator<Delim> it;
public:
    StringRange(char const* ptr) : it{ptr} {}
    auto begin() { return it;                      }
    auto end()   { return StringIterator<Delim>{}; }
};

int main()
{
    // "Hello World", no exclamation mark
    for (auto const& c : StringRange<'!'>{"Hello World!"})
        std::cout << c;
}

Live Example with g++ -std=c++14, (assembly using gcc.godbolt.org)

The above operator== for StringIterator<> is symmetric in its arguments and does not rely on whether the range-for is begin != end or end != begin (otherwise you could cheat and cut the code in half).

For simple iteration patterns, the compiler is able to optimize the convoluted logic inside operator==. Indeed, for the above example, the operator== is reduced to a single comparison. But will this continue to work for long pipelines of ranges and filters? Who knows. It is likely to require heroic optimization levels.

C++17 will relax the constraints which will simplify delimited ranges...

So where exactly does the simplification manifest itself? In operator==, which now has extra overloads taking an iterator/sentinel pair (in both orders, for symmetry). So the run time logic becomes compile time logic.

#include <iostream>

template <char Delim = 0>
struct StringSentinel {};

struct StringIterator
{
    char const* ptr = nullptr;   

    template <char Delim>
    friend auto operator==(StringIterator lhs, StringSentinel<Delim> rhs) {
        return *lhs.ptr == Delim;
    }

    template <char Delim>
    friend auto operator==(StringSentinel<Delim> lhs, StringIterator rhs) {
        return rhs == lhs;
    }

    template <char Delim>
    friend auto operator!=(StringIterator lhs, StringSentinel<Delim> rhs) {
        return !(lhs == rhs);
    }

    template <char Delim>
    friend auto operator!=(StringSentinel<Delim> lhs, StringIterator rhs) {
        return !(lhs == rhs);
    }

    auto& operator*()  {        return *ptr;  }
    auto& operator++() { ++ptr; return *this; }
};

template <char Delim = 0>
class StringRange
{
    StringIterator it;
public:
    StringRange(char const* ptr) : it{ptr} {}
    auto begin() { return it;                      }
    auto end()   { return StringSentinel<Delim>{}; }
};

int main()
{
    // "Hello World", no exclamation mark
    for (auto const& c : StringRange<'!'>{"Hello World!"})
        std::cout << c;
}

Live Example using g++ -std=c++1z (assembly using gcc.godbolt.org, which is almost identical to the previous example).

...and will in fact support fully general, primitive "D-style" ranges.

WG21 paper N4382 has the following suggestion:

C.6 Range Facade and Adaptor Utilities [future.facade]

1 Until it becomes trivial for users to create their own iterator types, the full potential of iterators will remain unrealized. The range abstraction makes that achievable. With the right library components, it should be possible for users to define a range with a minimal interface (e.g., current, done, and next members), and have iterator types automatically generated. Such a range facade class template is left as future work.

Essentially, this is equal to D-style ranges (where these primitives are called empty, front and popFront). A delimited string range with only these primitives would look something like this:

template <char Delim = 0>
class PrimitiveStringRange
{
    char const* ptr;
public:    
    PrimitiveStringRange(char const* c) : ptr{c} {}
    auto& current()    { return *ptr;          }
    auto  done() const { return *ptr == Delim; }
    auto  next()       { ++ptr;                }
};

If one does not know the underlying representation of a primitive range, how to extract iterators from it? How to adapt this to a range that can be used with range-for? Here's one way (see also the series of blog posts by @EricNiebler) and the comments from @T.C.:

#include <iostream>

// adapt any primitive range with current/done/next to Iterator/Sentinel pair with begin/end
template <class Derived>
struct RangeAdaptor : private Derived
{      
    using Derived::Derived;

    struct Sentinel {};

    struct Iterator
    {
        Derived*  rng;

        friend auto operator==(Iterator it, Sentinel) { return it.rng->done(); }
        friend auto operator==(Sentinel, Iterator it) { return it.rng->done(); }

        friend auto operator!=(Iterator lhs, Sentinel rhs) { return !(lhs == rhs); }
        friend auto operator!=(Sentinel lhs, Iterator rhs) { return !(lhs == rhs); }

        auto& operator*()  {              return rng->current(); }
        auto& operator++() { rng->next(); return *this;          }
    };

    auto begin() { return Iterator{this}; }
    auto end()   { return Sentinel{};     }
};

int main()
{
    // "Hello World", no exclamation mark
    for (auto const& c : RangeAdaptor<PrimitiveStringRange<'!'>>{"Hello World!"})
        std::cout << c;
}

Live Example using g++ -std=c++1z (assembly using gcc.godbolt.org)

Conclusion: sentinels are not just a cute mechanism to press delimiters into the type system, they are general enough to support primitive "D-style" ranges (which themselves may have no notion of iterators) as a zero-overhead abstraction for the new C++1z range-for.

這篇關(guān)于C++17 中新的基于范圍的 for 循環(huán)如何幫助 Ranges TS?的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!

【網(wǎng)站聲明】本站部分內(nèi)容來(lái)源于互聯(lián)網(wǎng),旨在幫助大家更快的解決問(wèn)題,如果有圖片或者內(nèi)容侵犯了您的權(quán)益,請(qǐng)聯(lián)系我們刪除處理,感謝您的支持!

相關(guān)文檔推薦

What do compilers do with compile-time branching?(編譯器如何處理編譯時(shí)分支?)
Can I use if (pointer) instead of if (pointer != NULL)?(我可以使用 if (pointer) 而不是 if (pointer != NULL) 嗎?)
Checking for NULL pointer in C/C++(在 C/C++ 中檢查空指針)
Math-like chaining of the comparison operator - as in, quot;if ( (5lt;jlt;=1) )quot;(比較運(yùn)算符的數(shù)學(xué)式鏈接-如“if((5<j<=1)))
Difference between quot;if constexpr()quot; Vs quot;if()quot;(“if constexpr()之間的區(qū)別與“if())
C++, variable declaration in #39;if#39; expression(C++,if 表達(dá)式中的變量聲明)
主站蜘蛛池模板: 亚洲免费黄色 | 成人a毛片 | 秋霞午夜鲁丝一区二区老狼 | 久久成人av | 亚洲精品欧美 | 黄色网av | 成人区精品一区二区婷婷 | 国产做受入口竹菊 | aaa一级片| 国产精品黄色片 | 热久久免费视频 | 欧美久久久久久久久 | 亚洲日本在线观看 | 黄色91网站| 日本一级淫片色费放 | 黄色一级片免费 | 国产传媒一区二区 | 欧美日韩国产成人 | 国产激情 | 精品视频久久 | 成人毛片100免费观看 | 秋霞午夜鲁丝一区二区老狼 | 久久草视频 | 91青青草 | 91国在线 | 可以免费看黄色的网站 | 韩国三级av | 福利小视频 | 免费av一区 | 福利在线看 | 婷婷综合视频 | 国产激情视频在线 | 日韩精品一区二区三区免费视频 | 99久久99 | 欧美精品一 | 日本成人精品 | 欧美精品一区在线 | 2017天天干 | 国产又黄又猛 | 中文一区二区 | 亚洲黄色网址 |