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

C++17 中新的基于范圍的 for 循環如何幫助 Ranges

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

問題描述

委員會改變了基于范圍的 for 循環:

  • C++11:

    <代碼>{汽車&&__range = range_expression ;for (auto __begin = begin_expr, __end = end_expr;__開始!= __結束;++__開始){range_declaration = *__begin;循環語句}}

  • 到 C++17:

    <代碼>{汽車&&__range = range_expression ;自動 __begin = begin_expr ;自動 __end = end_expr ;for ( ; __begin != __end; ++__begin) {range_declaration = *__begin;循環語句}}

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

解決方案

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

WG21 論文是 P0184R0 其動機如下:

<塊引用>

現有的基于范圍的 for 循環受到過度約束.結束迭代器永遠不會遞增、遞減或取消引用.需要它作為一個迭代器沒有實際用途.

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

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

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

#include 模板 結構體字符串迭代器{char const* ptr = nullptr;朋友自動運算符==(StringIterator lhs,StringIterator rhs){返回 lhs.ptr ?(rhs.ptr || (*lhs.ptr == Delim)) : (!rhs.ptr || (*rhs.ptr == Delim));}朋友自動運算符!=(StringIterator lhs,StringIterator rhs){返回 !(lhs == rhs);}自動&運算符*() { 返回 *ptr;}自動&運算符++(){++ptr;返回 *this;}};模板 類 StringRange{StringIterator它;上市:StringRange(char const* ptr) : it{ptr} {}自動開始(){ 返回它;}auto end() { return StringIterator{};}};int main(){//Hello World",沒有感嘆號for (auto const& c : StringRange<'!'>{"Hello World!"})std::cout <<C;}

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

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

對于簡單的迭代模式,編譯器能夠優化operator==內部的復雜邏輯.事實上,對于上面的例子,operator== 被簡化為一個單一的比較.但這會繼續適用于范圍和過濾器的長管道嗎?誰知道.很可能需要英雄般的優化級別.

C++17 將放寬約束,這將簡化分隔范圍...

那么簡化究竟體現在什么地方呢?在 operator== 中,現在有額外的重載采用迭代器/哨兵對(在兩個順序中,為了對稱).所以運行時邏輯變成了編譯時邏輯.

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

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

...實際上將支持完全通用的原始D 風格"范圍.

WG21 論文 N4382 有以下建議:

<塊引用>

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

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

本質上,這等于 D 風格的范圍(這些原語被稱為 emptyfrontpopFront).只有這些原語的分隔字符串范圍看起來像這樣:

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

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

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

現場示例 使用 g++ -std=c++1z(組裝使用 gcc.godbolt.org)

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

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.

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

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

相關文檔推薦

What do compilers do with compile-time branching?(編譯器如何處理編譯時分支?)
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;(比較運算符的數學式鏈接-如“if((5<j<=1)))
Difference between quot;if constexpr()quot; Vs quot;if()quot;(“if constexpr()之間的區別與“if())
C++, variable declaration in #39;if#39; expression(C++,if 表達式中的變量聲明)
主站蜘蛛池模板: 天天操天天摸天天爽 | 国产在线观看福利 | 国产精品美女久久久久久久久久久 | 狠狠色综合久久婷婷 | 麻豆国产精品777777在线 | 日日艹夜夜艹 | 久久这里有精品 | 中文在线一区二区 | 亚洲欧美日韩精品久久亚洲区 | 久久久久亚洲国产| 久久久99国产精品免费 | 一级片子 | 精品久久久久久久久久久久久久久久久 | 黄色三级免费 | 久草www | 日韩快播电影 | 99久久99久久精品国产片果冰 | 日韩欧美精品 | 色视频在线免费观看 | 极情综合网 | 天天操网 | 欧美一区二区大片 | 国产精品精品视频一区二区三区 | 久久精品国产久精国产 | 欧美一区二区三区在线观看 | 欧美一级二级在线观看 | 免费在线观看一区二区 | 国产在线精品一区二区 | 在线免费观看成年人视频 | 亚洲欧美一区二区三区1000 | 日韩色图视频 | 亚洲精品日韩欧美 | 天天干视频 | 成人三级av | 精品日韩在线 | 秋霞电影一区二区三区 | 国产日韩精品视频 | a黄在线观看 | 色视频在线播放 | 凹凸日日摸日日碰夜夜 | 成人二区 |