如何使我的自定义类型与“基于范围的 for 循环”一起使用?

2023-12-13

像现在很多人一样,我一直在尝试 C++11 带来的不同功能。我最喜欢的之一是“基于范围的 for 循环”。

我明白那个:

for(Type& v : a) { ... }

相当于:

for(auto iv = begin(a); iv != end(a); ++iv)
{
  Type& v = *iv;
  ...
}

然后begin()只是返回a.begin()用于标准集装箱。

但如果我想怎么办使我的自定义类型“基于范围的 for 循环”感知?

我应该专攻吗begin() and end()?

如果我的自定义类型属于命名空间xml,我应该定义xml::begin() or std::begin() ?

简而言之,这样做的指导方针是什么?


自问题(和大多数答案)发布以来,标准已更改在此缺陷报告的解决方案中.

制作方法for(:)循环处理你的类型X现在是两种方式之一:

  • 创建会员X::begin() and X::end()返回类似迭代器的东西

  • 创建一个自由函数begin(X&) and end(X&)返回类似于迭代器的东西,与您的类型位于同一名称空间中X

和类似的const变化。这对于实现缺陷报告更改的编译器和不实现缺陷报告更改的编译器都有效。

返回的对象实际上不必是迭代器。这for(:) loop,

for( range_declaration : range_expression )

与 C++ 标准的大部分部分不同的是指定扩展为等价于:

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

其中变量开头为__仅用于说明,并且begin_expr and end_expr是召唤的魔法begin/end

对 begin/end 返回值的要求很简单:必须重载 pre-++,确保初始化表达式有效,二进制!=可以在布尔上下文中使用,一元*返回一些你可以分配初始化的东西range_declaration并公开一个公共析构函数。

以与迭代器不兼容的方式执行此操作可能是一个坏主意,因为如果这样做,C++ 的未来迭代可能会相对漫不经心地破坏您的代码。

顺便说一句,该标准的未来修订很可能会允许end_expr返回与以下类型不同的类型begin_expr。这很有用,因为它允许“惰性结束”评估(如检测空终止),很容易优化为与手写 C 循环一样高效,以及其他类似的优点。


1 请注意for(:)循环将任何临时存储在auto&&变量,并将其作为左值传递给您。您无法检测是否正在迭代临时(或其他右值);这样的重载不会被调用for(:)环形。请参阅 n4527 中的 [stmt.ranged] 1.2-1.3。

² 拨打begin/end方法,或仅 ADL 查找自由函数begin/end, orC 风格数组支持的魔力。注意std::begin不会被调用,除非range_expression返回类型为 in 的对象namespace std或依赖于相同的。


In c++17range-for 表达式已更新

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

与类型__begin and __end已解耦。

这允许结束迭代器与开始迭代器的类型不同。您的最终迭代器类型可以是仅支持的“哨兵”!=与开始迭代器类型。

为什么这很有用的一个实际例子是,你的最终迭代器可以读取“检查你的char*看看它是否指向'0'" when == with a char*。这允许 C++ range-for 表达式在迭代 null 终止时生成最佳代码char* buffer.

struct null_sentinal_t {
  template<class Rhs,
    std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
  >
  friend bool operator==(Rhs const& ptr, null_sentinal_t) {
    return !*ptr;
  }
  template<class Rhs,
    std::enable_if_t<!std::is_same<Rhs, null_sentinal_t>{},int> =0
  >
  friend bool operator!=(Rhs const& ptr, null_sentinal_t) {
    return !(ptr==null_sentinal_t{});
  }
  template<class Lhs,
    std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
  >
  friend bool operator==(null_sentinal_t, Lhs const& ptr) {
    return !*ptr;
  }
  template<class Lhs,
    std::enable_if_t<!std::is_same<Lhs, null_sentinal_t>{},int> =0
  >
  friend bool operator!=(null_sentinal_t, Lhs const& ptr) {
    return !(null_sentinal_t{}==ptr);
  }
  friend bool operator==(null_sentinal_t, null_sentinal_t) {
    return true;
  }
  friend bool operator!=(null_sentinal_t, null_sentinal_t) {
    return false;
  }
};

活生生的例子这个的。

最小测试代码是:

struct cstring {
  const char* ptr = 0;
  const char* begin() const { return ptr?ptr:""; }// return empty string if we are null
  null_sentinal_t end() const { return {}; }
};

cstring str{"abc"};
for (char c : str) {
    std::cout << c;
}
std::cout << "\n";

这是一个简单的例子。

namespace library_ns {
  struct some_struct_you_do_not_control {
    std::vector<int> data;
  };
}

你的代码:

namespace library_ns {
  int* begin(some_struct_you_do_not_control& x){ return x.data.data(); }
  int* end(some_struct_you_do_not_control& x){ return x.data.data()+x.data.size(); }
  int const* cbegin(some_struct_you_do_not_control const& x){ return x.data.data(); }
  int* cend(some_struct_you_do_not_control const& x){ return x.data.data()+x.data.size(); }
  int const* begin(some_struct_you_do_not_control const& x){ return cbegin(x); }
  int const* end(some_struct_you_do_not_control const& x){ return cend(x); }
}

这是一个如何将您无法控制的类型增强为可迭代的示例。

在这里,我将指针作为迭代器返回,隐藏了我在引擎盖下有一个向量的事实。

对于您拥有的类型,您可以添加方法:

struct egg {};
struct egg_carton {
  auto begin() { return eggs.begin(); }
  auto end() { return eggs.end(); }
  auto cbegin() const { return eggs.begin(); }
  auto cend() const { return eggs.end(); }
  auto begin() const { return eggs.begin(); }
  auto end() const { return eggs.end(); }
private:
  std::vector<egg> eggs;
};

在这里我重用了vector的迭代器。我用auto为简洁起见;在c++11我必须说得更详细。

这是一个快速而肮脏的可迭代范围视图:

template<class It>
struct range_t {
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }

  std::size_t size() const
  // C++20 only line: (off C++20 it generates a hard error)
  requires std::random_access_iterator<It>
  {
    return end()-begin(); // do not use distance: O(n) size() is toxic
  }

  bool empty() const { return begin()==end(); }
 
  range_t without_back() const {
    if(emptty()) return *this;
    return {begin(), std::prev(end())};
  }

  range_t without_back( std::size_t n ) const
  // C++20 only line: (see below)
  requires !std::random_access_iterator<It>
  {
    auto r=*this;
    while(n-->0 && !r.empty())
      r=r.without_back();
    return r;
  }

  range_t without_front() const {
    if(empty()) return *this;
    return {std::next(begin()), end()};
  }

  range_t without_front( std::size_t n ) const
  // C++20 only line: (see below)
  requires !std::random_access_iterator<It>
  {
    auto r=*this;
    while(n-->0 && !r.empty())
      r=r.without_front();
    return r;
  }

  // C++20 section:
  range_t without_back( std::size_t n ) const
  requires std::random_access_iterator<It>
  {
    n = (std::min)(n, size());
    return {b, e-n};
  }
  range_t without_front( std::size_t n ) const
  requires std::random_access_iterator<It>
  {
    n = (std::min)(n, size());
    return {b+n, e};
  }
  // end C++20 section


  decltype(auto) front() const { return *begin(); }
  decltype(auto) back() const { return *(std::prev(end())); }
};
template<class It>
range_t(It,It)->range_t<It>;
template<class C>
auto make_range( C&& c ) {
  using std::begin; using std::end;
  return range_t{ begin(c), end(c) };
}

using c++17模板类推导。

std::vector<int> v{1,2,3,4,5};
for (auto x : make_range(v).without_front(2) ) {
  std::cout << x << "\n";
}

打印 3 4 5,跳过第一个 2。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使我的自定义类型与“基于范围的 for 循环”一起使用? 的相关文章

  • c和java语言中的换行符

    现在行分隔符取决于系统 但在 C 程序中我使用 n 作为行分隔符 无论我在 Windows 还是 Linux 中运行它都可以正常工作 为什么 在java中 我们必须使用 n 因为它与系统相关 那么为什么我们在c中使用 n 作为新行 而不管我
  • 在 C# 中创建具有单独列的分隔文本

    我一直在尝试在 C 中创建一个制表符限制的文本文件 以便数据正确显示在单独的列中 Firstname Lastname Age John Smith 17 James Sawyer 31 我尝试过 t 字符 但我得到的只是 Firstnam
  • VB.NET 相当于 C# 属性简写吗?

    是否有与 C 等效的 VB NET public string FirstName get set 我知道你能做到 Public Property name As String Get Return name ToString End Ge
  • 互斥体实现可以互换(独立于线程实现)

    所有互斥体实现最终都会调用相同的基本系统 硬件调用吗 这意味着它们可以互换吗 具体来说 如果我使用 gnu parallel算法 使用openmp 并且我想让他们称之为线程安全的类我可以使用boost mutex用于锁定 或者我必须编写自己
  • XamlReader.Load 在后台线程中。是否可以?

    WPF 应用程序具有从单独的文件加载用户控件的操作 使用XamlReader Load method StreamReader mysr new StreamReader pathToFile DependencyObject rootOb
  • 单元测试一起运行时失败,单独运行时通过

    所以我的单元测试遇到了一些问题 我不能只是将它们复制并粘贴到这里 但我会尽力而为 问题似乎是 如果我一项一项地运行测试 一切都会按预期进行 但如果我告诉它一起运行测试 则 1 5 将通过 TestMethod public void Obj
  • 在 C# 中循环遍历文件文件夹的最简单方法是什么?

    我尝试编写一个程序 使用包含相关文件路径的配置文件来导航本地文件系统 我的问题是 在 C 中执行文件 I O 这将是从桌面应用程序到服务器并返回 和文件系统导航时使用的最佳实践是什么 我知道如何谷歌 并且找到了几种解决方案 但我想知道各种功
  • 无法在 Windows 运行时组件库的 UserControl 中创建依赖项属性

    我想在用户控件内创建数据可绑定属性 这个用户控件包含一个 Windows 运行时组件 项目 我使用下面的代码来创建属性 public MyItem CurrentItem get return MyItem GetValue Current
  • 获取 WPF 控件的所有附加事件处理程序

    我正在开发一个应用程序 在其中动态分配按钮的事件 现在的问题是 我希望获取按钮单击事件的所有事件 因为我希望删除以前的处理程序 我尝试将事件处理程序设置为 null 如下所示 Button Click null 但是我收到了一个无法分配 n
  • 未经许可更改内存值

    我有一个二维数组 当我第一次打印数组的数据时 日期打印正确 但其他时候 array last i 的数据从 i 0 到 last 1 显然是一个逻辑错误 但我不明白原因 因为我复制并粘贴了 for 语句 那么 C 更改数据吗 I use g
  • 如何将整数转换为 void 指针?

    在 C 中使用线程时 我面临警告 警告 从不同大小的整数转换为指针 代码如下 include
  • PlaySound 可在 Visual Studio 中运行,但不能在独立 exe 中运行

    我正在尝试使用 Visual Studio 在 C 中播放 wav 文件 我将文件 my wav 放入项目目录中并使用代码 PlaySound TEXT my wav NULL SND FILENAME SND SYNC 我按下播放按钮 或
  • 上下文敏感与歧义

    我对上下文敏感性和歧义如何相互影响感到困惑 我认为正确的是 歧义 歧义语法会导致使用左推导或右推导构建多个解析树 所有可能的语法都是二义性的语言是二义性语言 例如 C 是一种不明确的语言 因为 x y 总是可以表示两个不同的事物 如下所述
  • 等待线程完成

    private void button1 Click object sender EventArgs e for int i 0 i lt 15 i Thread nova new Thread Method nova Start list
  • (de)从 CSV 序列化为对象(或者最好是类型对象的列表)

    我是一名 C 程序员 试图学习 C 似乎有一些内置的对象序列化 但我在这里有点不知所措 我被要求将测试数据从 CSV 文件加载到对象集合中 CSV 比 xml 更受青睐 因为它更简单且更易于人类阅读 我们正在创建测试数据来运行单元测试 该集
  • C++ 密码屏蔽

    我正在编写一个代码来接收密码输入 下面是我的代码 程序运行良好 但问题是除了数字和字母字符之外的其他键也被读取 例如删除 插入等 我知道如何避免它吗 特q string pw char c while c 13 Loop until Ent
  • Server.MapPath - 给定的物理路径,预期的虚拟路径

    我正在使用这行代码 var files Directory GetFiles Server MapPath E ftproot sales 在文件夹中查找文件 但是我收到错误消息说 给定物理路径但虚拟路径 预期的 我对在 C 中使用 Sys
  • 使用 GROUP 和 SUM 的 LINQ 查询

    请帮助我了解如何使用带有 GROUP 和 SUM 的 LINQ 进行查询 Query the database IEnumerable
  • 检查Windows控制台中是否按下了键[重复]

    这个问题在这里已经有答案了 可能的重复 C 控制台键盘事件 https stackoverflow com questions 2067893 c console keyboard events 我希望 Windows 控制台程序在按下某个
  • 在客户端系统中安装后桌面应用程序无法打开

    我目前正在使用 Visual Studio 2017 和 4 6 1 net 框架 我为桌面应用程序创建了安装文件 安装程序在我的系统中完美安装并运行 问题是安装程序在其他计算机上成功安装 但应用程序无法打开 edit 在客户端系统中下载了

随机推荐

  • 如何创建多个空数据框?

    而不是这样做 a pd DataFrame d pd DataFrame c pd DataFrame d pd DataFrame e pd DataFrame 一次 有没有一种快速方法可以用空数据框初始化所有变量 因为最终我想使用 fo
  • 使用反应扩展每秒按顺序获取下一个事件

    我有以下类型 public class NewsFeed public event EventHandler
  • 通过内置 Web 服务将文件上传到 SharePoint

    通过 WSS 3 0 版本公开的内置 Web 服务将文件上传到 SharePoint 服务器上的文档库的最佳方法是什么 根据两个最初的答案 我们肯定需要使用 Web 服务层 因为我们将从远程客户端应用程序进行这些调用 WebDAV 方法适合
  • 如何在 C# 中使工具提示指向特定标签?

    在我的应用程序中 我想使用工具提示来指向标签以引起用户的注意 toolTip IsBalloon true toolTip Show message label1 问题是气球没有指向指定的标签 我应该怎么办 这是一个已知的错误 尝试调用它两
  • 无法将焦点设置为 Chrome 扩展中的输入

    由于某种原因 我无法将焦点设置在 popup html 中的文本框上 到目前为止 这是我尝试过的 弹出 html
  • 类型名称与变量类型

    我想检查 a 的类型Variant 这是可以做到的TypeName and VarType 我想使用VarType效率更高 因为它不涉及字符串比较 仅涉及数字比较 任何偏爱的理由TypeName Public Sub testType Di
  • 指南针编译时间太长

    自从更新到最新版本的 Compass 以来 编译现在需要 4 294 秒 由于需要 我需要这个版本的指南针susy Running sass dist sass task Running watch task Completed in 4
  • 如何使用 JavaScript 发送电子邮件?

    如何使用 JavaScript 发送电子邮件 我不想使用mailto 因为如果我使用mailto 它会打开一个电子邮件客户端 做不到 发送电子邮件仅适用于服务器端 如果您所做的只是发送电子邮件 那么快速PHP脚本可能会达到目的
  • Delphi Tokyo 中不再使用 Form.PixelsPerInch

    在较旧的 Delphi 中 例如 Delphi XE4 很清楚Form PixelsPerInch财产是为了 如果我更改此属性并保存表单 关闭然后重新加载它 则表单上的所有控件和字体将相应地调整大小 然而 在德尔福东京 也许自西雅图以来 进
  • 无效的 Geojson 对象 Angularjs &Leafletjs

    在我的项目中 我尝试将过滤器与表和地图上显示的 geojson 同步 为了实现这一目标 我使用了 Angular 和以前的 Angular Leaflet Directive 但性能对于我的目的来说很慢 所以我决定为 Leaflet js
  • 如何在不同的语言环境下使用 PostgreSQL upper() 函数?

    我在共享主机上有一个 PostgreSQL 数据库 使用的结果upper由于区域设置的原因 我的本地数据库中的函数有所不同 这是我想要的 并且在我的本地环境中拥有 SELECT version PostgreSQL 8 4 16 on i3
  • Python-根据列值将数据帧拆分为多个数据帧并用这些值命名它们[重复]

    这个问题在这里已经有答案了 我有一个大型数据集 列出了在全国不同地区销售的竞争对手产品 我希望通过使用这些新数据帧名称中的列值的迭代过程 根据区域将该数据帧拆分为其他几个数据帧 以便我可以单独处理每个数据帧 例如按价格对每个地区的信息进行排
  • Python Turtle 图形键盘命令

    有人对使用键盘命令在 python 2 7 中控制海龟图形有任何见解吗 我在这个网站和其他网站上进行了广泛的研究 觉得我正在做正确的事情 但它只是不想为我工作 以下是我到目前为止所得到的 谁能告诉我哪里出错了 from turtle imp
  • Django 模板过滤器 - 一行

    我正在寻找一个 Django 模板过滤器 它将多行结构转变为一大行 有人实施过吗 原因是 我有一个表单 form as p 创建一个多行html片段 我想创建一个javascript变量 它是一个html片段 但是当我这样做时 var ne
  • 启用和禁用 gridview 上的链接按钮

    我想根据条件在 gridview 的某些行上启用或禁用链接按钮 我可以在一行上启用 linkbutton 并在同一网格视图的另一行上禁用它吗 我的代码在这里 protected void GridView1 RowDataBound obj
  • 使用 regex_search 获取所有匹配项的索引?

    我刚刚开始学习如何使用regex用于字符串处理 C 11新功能 如果以下问题太愚蠢 请原谅我 目前我应用以下代码来获取所有匹配项的索引 string str aaabxxxaab regex rx ab vector
  • 为什么 3 和 x(被分配为 3)在 Haskell 中具有不同的推断类型? [复制]

    这个问题在这里已经有答案了 Haskell 中的类型推断有一点学习曲线 至少可以这么说 开始学习它的一个好方法是通过简单的例子 因此 以下是类型推断的 hello world 考虑以下示例 Prelude gt t 3 3 Num t gt
  • 无符号整数溢出

    将会发生什么unsigned int当我溢出它时包含它 具体来说 我想与两个相乘unsigned ints 里面会有什么unsigned int乘法完成后 unsigned int someint 253473829 13482018273
  • 为什么不能使用 memcmp() 函数比较浮点类型?

    bool floatcmp const float a const float b const void p void a const void q void b if memcmp p q sizeof float 0 return tr
  • 如何使我的自定义类型与“基于范围的 for 循环”一起使用?

    像现在很多人一样 我一直在尝试 C 11 带来的不同功能 我最喜欢的之一是 基于范围的 for 循环 我明白那个 for Type v a 相当于 for auto iv begin a iv end a iv Type v iv 然后be