如何让我的自定义类型使用“基于范围的循环”? [英] How to make my custom type to work with "range-based for loops"?

查看:78
本文介绍了如何让我的自定义类型使用“基于范围的循环”?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

像许多人这些天,我一直在尝试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

c + + 17rel =tag> c ++ 17 已更新表达式的范围 p>

  {
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() and X::end() that return something that acts like an iterator

  • Create a free function begin(X&) and end(X&) that return something that acts like an iterator, in the same namespace as your type X

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 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆