如何让我的自定义类型使用“基于范围的循环”? [英] How to make my custom type to work with "range-based for loops"?
问题描述
像许多人这些天,我一直在尝试C + 11带来的不同功能。我最喜欢的是基于范围的for循环。
我理解:
for(Type& v:a){...}
等价于:
for(auto iv = begin(a); iv!= end(a); ++ iv)
{
类型& v = * iv;
...
}
()只返回 a.begin()
。
我应该专注于如果我想要自定义类型基于范围的for循环 begin()
和 end()
?
如果我的自定义类型属于namespace xml
,应该定义 xml :: begin()
或 std :: begin )
简单来说,有什么准则?
标准已经更改,因为问题(和大多数答案)已发布。
/ code>循环处理您的类型
X
现在是以下两种方法之一:
-
创建成员
X :: begin()
和X :: end()
-
创建一个自由函数
begin(X&)
$ c> end(X&)在类型X
的同一命名空间中返回类似迭代器的内容。 >
与 const
变体类似。这将在实现缺陷报告更改的编译器和不需要编译器的编译器上工作。
返回的对象实际上不必是迭代器。与C ++标准的大多数部分不同, for(:)
循环是指定要展开为等同于的内容:
for(range_declaration:range_expression)
变为:
{
auto&&& __range = range_expression;
for(auto __begin = begin_expr,
__end = end_expr;
__begin!= __end; ++ __ begin){
range_declaration = * __ begin;
loop_statement
}
}
code> __ 仅适用于exposition, begin_expr
和 end_expr
魔法调用开始
/ 结束
.²
对开始/结束返回值的要求很简单:必须重载pre - ++
,确保初始化表达式有效,binary !=
,可以在布尔上下文中使用,一元 *
,返回可以分配的内容initialize range_declaration
with,并公开一个公开的析构函数。
这样做与迭代器不兼容可能是一个坏主意,因为未来的C ++迭代可能
除此之外,标准的未来修订很可能允许 end_expr
返回与 begin_expr
不同的类型。这是有用的,因为它允许惰性端评估(如检测空终止),易于优化以与手写C循环一样高效,以及其他类似的优点。
¹请注意, for(:)
auto&&& 变量,并将其作为左值传递给您。你不能检测是否是迭代临时(或其他右值);这样的重载不会被 for(:)
循环调用。从n4527查看[stmt.ranged] 1.2-1.3。
可以调用 begin
/ end
方法或仅ADL的自由函数查找 begin
/ end
或魔术的C风格阵列支持。注意,除非 range_expression
返回命名空间中的类型的对象,否则不会调用
std :: begin
{
auto&& __range = range_expression;
auto __begin = begin_expr;
auto __end = end_expr
for(; __ begin!= __end; ++ __ begin){
range_declaration = * __ begin;
loop_statement
}
}
c $ c> __ begin 和 __ end
已解除连结。
迭代器与开始不是相同的类型。您的结束迭代器类型可以是一个sentinal,它只支持使用begin迭代器类型的!=
。
A为什么这是有用的实际示例是,你的结束迭代器可以读取检查您的 char *
,看看它是否指向'0'
==
与 char *
这允许C ++范围为表达式在遍历一个nul终止的 char *
缓冲区时生成最优代码。
Like many people these days I've been trying the different features that C+11 brings. One of my favorites is the "range-based for loops".
I understand that:
for(Type& v : a) { ... }
Is equivalent to:
for(auto iv = begin(a); iv != end(a); ++iv)
{
Type& v = *iv;
...
}
And that begin()
simply returns a.begin()
for standard containers.
But what if I want to make my custom type "range-based for loop"-aware ?
Should I just specialize begin()
and end()
?
If my custom type belongs to the namespace xml
, should I define xml::begin()
or std::begin()
?
In short, what are the guidelines to do that ?
The standard has been changed since the question (and most answers) were posted in the resolution of this defect report.
The way to make a for(:)
loop work on your type X
is now one of two ways:
Create member
X::begin()
andX::end()
that return something that acts like an iteratorCreate a free function
begin(X&)
andend(X&)
that return something that acts like an iterator, in the same namespace as your typeX
.¹
And similar for const
variations. This will work both on compilers that implement the defect report changes, and compilers that do not.
The objects returned do not have to actually be iterators. The for(:)
loop, unlike most parts of the C++ standard, is specified to expand to something equivalent to:
for( range_declaration : range_expression )
becomes:
{
auto && __range = range_expression ;
for (auto __begin = begin_expr,
__end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
where the variables beginning with __
are for exposition only, and begin_expr
and end_expr
is the magic that calls begin
/end
.²
The requirements on the begin/end return value are simple: You must overload pre-++
, ensure the initialization expressions are valid, binary !=
that can be used in a boolean context, unary *
that returns something you can assign-initialize range_declaration
with, and expose a public destructor.
Doing so in a way that isn't compatible with an iterator is probably a bad idea, as future iterations of C++ might be relatively cavalier about breaking your code if you do.
As an aside, it is reasonably likely that a future revision of the standard will permit end_expr
to return a different type than begin_expr
. This is useful in that it permits "lazy-end" evaluation (like detecting null-termination) that is easy to optimize to be as efficient as a hand-written C loop, and other similar advantages.
¹ Note that for(:)
loops store any temporary in an auto&&
variable, and pass it to you as an lvalue. You cannot detect if you are iterating over a temporary (or other rvalue); such an overload will not be called by a for(:)
loop. See [stmt.ranged] 1.2-1.3 from n4527.
² Either call the begin
/end
method, or ADL-only lookup of free function begin
/end
, or magic for C-style array support. Note that std::begin
is not called unless range_expression
returns an object of type in namespace std
or dependent on same.
In c++17 the range-for expression has been updated
{
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr
for (;__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
with the types of __begin
and __end
have been decoupled.
This permits the end iterator to not be the same type as begin. Your end iterator type can be a "sentinal" wich only supports !=
with the begin iterator type.
A practical example of why this is useful is that your end iterator can read "check your char*
to see if it points to '0'
" when ==
with a char*
. This allows a C++ range-for expression to generate optimal code when iterating over a nul-terminated char*
buffer.
这篇关于如何让我的自定义类型使用“基于范围的循环”?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!