gpt4 book ai didi

c++ - C++17 中新的基于范围的 for 循环如何帮助 Ranges TS?

转载 作者:IT老高 更新时间:2023-10-28 13:20:33 26 4
gpt4 key购买 nike

委员会将基于范围的 for 循环从:

  • C++11:

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

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

人们说这将使 Ranges TS 的实现更容易。你能给我一些例子吗?

最佳答案

C++11/14 范围-for被过度约束...

WG21 论文是 P0184R0其动机如下:

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.

从您发布的标准语中可以看出,end范围的迭代器仅用于循环条件 __begin != __end; .因此end只需与 begin 相等即可,并且它不需要是可取消引用或可递增的。

...扭曲了operator==用于分隔的迭代器。

那么这有什么缺点呢?好吧,如果你有一个标记分隔的范围(C 字符串、文本行等),那么你必须将循环条件硬塞到迭代器的 operator== 中。 ,本质上是这样的

#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使用 g++ -std=c++14,(assembly 使用 gcc.godbolt.org)

以上operator==对于 StringIterator<>它的论点是对称的,并且不依赖于 range-for 是否为 begin != endend != begin (否则你可以作弊并将代码切成两半)。

对于简单的迭代模式,编译器能够优化 operator== 内部的复杂逻辑。 .事实上,对于上面的例子,operator==简化为单一比较。但这会继续适用于范围和过滤器的长管道吗?谁知道。它可能需要英雄优化级别。

C++17 将放宽限制以简化分隔范围...

那么简化究竟体现在哪里?在 operator== ,它现在有额外的重载,采用迭代器/哨兵对(在两个顺序中,为了对称)。所以运行时逻辑变成了编译时逻辑。

#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使用 g++ -std=c++1z (assembly 使用 gcc.godbolt.org,与前面的示例几乎相同)。

...实际上将支持完全通用的原始“D 样式”范围。

WG21 论文 N4382有以下建议:

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.

本质上,这等于 D 样式范围(这些原语称为 emptyfrontpopFront )。只有这些原语的分隔字符串范围看起来像这样:

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; }
};

如果不知道原始范围的底层表示,如何从中提取迭代器?如何使其适应可与 range- for 一起使用的范围?这是一种方法(另见@EricNiebler 的series of blog posts)和@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使用 g++ -std=c++1z (assembly 使用 gcc.godbolt.org)

结论:哨兵不只是一种将分隔符压入类型系统的可爱机制,它们对于support primitive "D-style" ranges 来说足够通用。 (它们本身可能没有迭代器的概念)作为新 C++1z range-for 的零开销抽象。

关于c++ - C++17 中新的基于范围的 for 循环如何帮助 Ranges TS?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39117330/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com