c++ 17中新的基于范围的for循环如何帮助Ranges TS
How the new range-based for loop in C++17 helps Ranges TS?
委员会将基于范围的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 range- for
was overconstrained…
WG21论文为P0184R0,其动机如下:
现有的基于范围的for循环是过度约束的。结束迭代器永远不会进行自增、自减或解引用操作。要求作为一个迭代器是没有实际用途的。
从你发布的标准中可以看到,范围的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;
}
<<p> 生活例子/strong>用g++化c++ 14日(组装使用gcc.godbolt.org) 上面的operator==
for StringIterator<>
的参数是对称的,不依赖于range-for是begin != end
还是end != 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;
}
现场示例使用g++ -std=c++1z (assembly使用gcc.godbolt.org,这几乎与上一个示例相同)。
…并且实际上将完全支持通用的、原始的"D-style"范围。
WG21论文N4382有如下建议:C.6范围外立面和适配器实用程序[未来]。facade)
1直到它对于用户来说,创建自己的迭代器类型变得微不足道迭代器的潜力仍未实现。范围抽象这是可以实现的。如果有合适的库组件,应该是这样用户可以用最小的接口定义范围(例如:
current
、done
和next
成员),并且具有迭代器类型自动生成。这样的范围facade类模板保留为未来的工作。
本质上,这等于d风格范围(这些原语被称为empty
, front
和popFront
)。只有这些原语的分隔字符串范围看起来像这样:
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; }
};
如果不知道基本范围的底层表示,如何从中提取迭代器?如何适应这个范围,可以使用范围- for
?这里有一种方法(参见@EricNiebler的系列博客文章)和@ 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;
}
使用g++ -std=c++1z (assembly using gcc.godbolt.org)
:哨兵不仅仅是一种将分隔符压入类型系统的可爱机制,它们足够通用,支持原始的"d风格"范围(它们本身可能没有迭代器的概念),作为新的c++ 1z范围的零开销抽象-for.
新的规范允许__begin
和__end
是不同的类型,只要__end
可以与__begin
进行不相等的比较。__end
甚至不需要是迭代器,可以是谓词。下面是一个愚蠢的例子,其中一个结构体定义了begin
和end
成员,后者是谓词而不是迭代器:
#include <iostream>
#include <string>
// a struct to get the first word of a string
struct FirstWord {
std::string data;
// declare a predicate to make ' ' a string ender
struct EndOfString {
bool operator()(std::string::iterator it) { return (*it) != ' ' && (*it) != ' '; }
};
std::string::iterator begin() { return data.begin(); }
EndOfString end() { return EndOfString(); }
};
// declare the comparison operator
bool operator!=(std::string::iterator it, FirstWord::EndOfString p) { return p(it); }
// test
int main() {
for (auto c : {"Hello World !!!"})
std::cout << c;
std::cout << std::endl; // print "Hello World !!!"
for (auto c : FirstWord{"Hello World !!!"}) // works with gcc with C++17 enabled
std::cout << c;
std::cout << std::endl; // print "Hello"
}
- Python中的for循环与C++有何不同
- 请求有关C++中嵌入 for 循环的帮助
- 需要帮助编写 for 循环以在字符串周围创建边框
- 需要循环帮助以迭代方式添加到程序集中的总和变量
- 需要帮助编写一个小程序来将循环乘以 n 个元素和 k 个多重性
- 需要帮助定义循环控制变量(while loops)C++
- 需要有关无限循环的帮助
- 需要帮助在 C++ 中使用嵌套 for 循环
- 需要帮助停止循环
- 需要帮助转换以执行循环
- 我用C++做了一个计算器,但我对"type of calculator"的要求循环了两次。有人可以帮助指出我的错误吗?
- C++帮助:如何停止 while 循环并得出总和
- 我确实需要一些帮助来创建显示信息的循环
- 需要井字游戏中的 for 循环帮助
- 需要帮助创建一个休眠条件的循环,当满足条件时,它会停止,直到下一个操作
- 需要帮助优化C++中循环/点积函数的速度
- 我需要帮助在我的循环中获得新的平均值
- c++中的循环依赖帮助
- c++ 17中新的基于范围的for循环如何帮助Ranges TS
- 需要关于数组和循环的帮助