基于 C++17 中的范围,用于自定义容器或具有不同开始/结束类型的通用类
Posted
技术标签:
【中文标题】基于 C++17 中的范围,用于自定义容器或具有不同开始/结束类型的通用类【英文标题】:Range based for in C++17 for custom container or general classes with different begin/end types 【发布时间】:2019-05-23 18:37:00 【问题描述】:我正在使用 g++ v8.2 和 -std=c++2a -Wall- -Wextra -pedantic 在 Raspberry Pi 3B+ 上运行。我正在尝试更好地了解我的自定义容器的基于循环的范围。
您在下面看到一个类,它实现了一个基于范围的自定义类。那工作正常。然而。我对我的工作实施有一些额外的具体问题。
我已经检查过this 和其他人。但这些都没有回答我的问题。
代码如下:
#include <iostream>
struct LinkedList // Simplest linked list
int k;
LinkedList *next;
;
// For test pruposes: Build manually linked list as globals
LinkedList aa33,nullptr; LinkedList aa22,&aa3; LinkedList aa11,&aa2;
class Example
public:
Example &begin (void) linkedList = linkedListStartAddress; return *this;
int end (void) return 0;
LinkedList &operator * (void) return *linkedList;
void operator ++ (void) linkedList = linkedList->next;
bool operator != (const int&) return (linkedList != nullptr);
protected:
LinkedList *linkedListStartAddress &aa1; // Global initialisation for test purposes
LinkedList *linkedList&aa1; // Global initialisation for test purposes
;
int main(void)
Example example;
for (auto l : example)
std::cout << l.k << '\n';
return 0;
好的,这行得通。
循环的一般定义是:
auto && __range = range_expression ;
auto __begin = begin_expr;
auto __end = end_expr
for (;__begin != __end; ++__begin)
range_declaration = *__begin;
loop_statement
这很难理解。 __range 和 range_expression 都不会在后面使用。在这种情况下,“汽车”对我来说也很困难。我看不到类型。现在我的假设和问题。
-
示例类同时是容器和迭代器。这是一个正确或常见的用例吗?
显然“begin”的返回类型必须是“range_expression”的类型,反之亦然。正确吗?
'begin' 的返回类型必须是类类型,否则将不会调用运算符(++、*、!=)。正确吗?
调用“开始”函数只是为了进行初始化。并返回对该类的引用。它不一定与底层容器有关系。正确吗?
'end' 函数(我检查过:它在 'begin' 之后立即调用。然后再也不会调用)没有任何意义 (c++17)。它只是在那里定义(及其返回值)'operator !='右侧的类型。正确吗?
如何将“运算符!=”的类型推断为“const 'ReturnTypeOfEnd' &”。我尝试使用 decltype,但失败了。
显然,'operator*' 的返回类型定义了 range_declaration 的类型。在我的情况下,'*__begin' 的类型与 '__begin' 完全不同。不是地址什么的。好吗?
如果您能对此有所了解,我会很高兴。 . .
【问题讨论】:
auto && __range = range_expression
仅表示对范围表达式进行评估。那是“for (x:y)
”的“y”部分。您可以将begin_expr
和end_expr
视为基本上std::begin(__range)
和std::end(__range)
。这几乎就是胶囊摘要,不超过 600 个字符。
重复的***.com/q/8164567/2466431?
How to make my custom type to work with "range-based for loops"?的可能重复
我在提问之前阅读了这两篇文章。当我输入标题时,这些帖子已显示为可能的答案。但对我来说,这个话题还不清楚,所以我特地问了。很乐意得到一些额外的具体答案。谢谢
@Armin:方式 这个问题中有太多单独的、不相关的问题。如果有不清楚的地方,作为关于该特定事物的问题。您真正想要的是对范围 for
的工作原理的逐步重新解释,仅限于这个特定用例。
【参考方案1】:
您的解决方案的主要问题是您尝试以非标准的方式进行处理,这种方式有很多限制,并且可能难以被那些对迭代器在 STL 中如何工作有深入了解的专家使用。
我将尝试以一种技术含量较低的方式回答大多数问题,这应该有助于理解它在实践中的工作原理。
1) 共享类型会导致一些问题,因为某些算法可能需要多个活动迭代器。此外,它不尊重 SRP(单一责任原则),因此不是一个好的设计实践。
2) 它只需要返回本质上类似于迭代器的东西。
3) 或者如果数据在内存中是连续的,它可以是一个指针。
4) 通常begin
函数按值返回一个迭代器。
5) 通常end
函数会返回一个迭代器到末尾。如果 end 不是真正的位置(例如输入流或容器的最后一个值是哨兵),它也可以返回哨兵对象。
6) 您可以将自己的 typedef/aliases 放入类中并根据需要使用它们。
7) 运算符 * 的返回值类型几乎总是与迭代器类型不同。
然后是一些意见/建议
在您的情况下,LinkedList
将是迭代器类型(或者您可以使用包装器)。
如果您希望能够知道大小而不必迭代整个列表,则通常您希望容器不仅仅是一个迭代器。此外,容器可能会提供一些优化的成员函数,例如 std::list
中的 sort
。
如果您想了解专家的做法,STL 源代码可能是一个很好的来源。
您的代码不尊重constness
,因此如果您将Example example;
替换为const Example example;
,将无法正常工作。
您的大多数运算符在声明中不遵循通常的约定。
您的begin
函数有副作用。
使用您的代码,您将无法将迭代器存储到列表中的某个位置。这意味着排序或删除匹配项等算法可能会失败。
将void
放在空参数列表中本质上是一种过时的方式来编写不应再在C++ 中使用的代码。它只在 C 中有用。
Operator++ 通常应该返回对此的引用。
如果您想使用哨兵作为最终值,最好使用您自己的类型(enum
值可以做到)。否则,它会允许意外比较像 example != 25
那样编译(在这种情况下,当 k
的值为 25 时,循环可能会结束),因此这会使代码更难理解。
为什么不使用std::forward_list
而不是重新发明***。
如果您确实需要使用 LinkedList
,那么 STL 可能是有关如何正确定义迭代器的宝贵信息来源。
【讨论】:
非常感谢您的评价和提示。只是为了澄清:提供的代码只是一个例子。纯粹用于测试目的。我想了解基于 for 的范围如何在后台工作。再次感谢以上是关于基于 C++17 中的范围,用于自定义容器或具有不同开始/结束类型的通用类的主要内容,如果未能解决你的问题,请参考以下文章
用于匹配单词的 javascript 正则表达式模式,具有自定义单词边界
用于解释自定义 c++11 属性的 Clang/GCC 插件
基于不适用于 System.Web.UI.Control 对象的 CA2000“在失去范围之前处理对象”创建自定义 FXCop 规则