基于C ++ 17的范围,适用于具有不同开始/结束类型的自定义容器或常规类

时间:2018-12-24 17:14:10

标签: c++ for-loop iterator range c++17

我在Raspberry Pi 3B +上运行带有-std = c ++ 2a -Wall- -Wextra -pedantic的g ++ v8.2。我试图更好地了解自定义容器的基于范围的循环。

下面您将看到一个实现自定义类的类,该类具有基于的范围。很好然而。我在执行工作时还有其他一些具体问题。

我也已经检查this和其他人。但是这些都没有回答我的问题。

代码如下:

#include <iostream>
struct LinkedList  // Simplest linked list
{
    int k;  
    LinkedList *next;
};
// For test pruposes: Build manually linked list as globals
LinkedList aa3{3,nullptr};  LinkedList aa2{2,&aa3};  LinkedList aa1{1,&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。在这种情况下,“自动”对我来说也很困难。我看不到类型。现在我的假设和问题。

  1. 示例类同时是容器和迭代器。这是正确或常见的用例吗?
  2. 很显然,“ begin”的返回类型必须是“ range_expression”的类型,反之亦然。正确吗?
  3. 'begin'的返回类型必须是类类型,否则将不会调用运算符(++,*,!=)。正确吗?
  4. 仅出于初始化目的调用“ begin”功能。并返回对该类的引用。它不一定与基础容器有关系。正确吗?
  5. “结束”功能(我检查过:在“开始”之后立即调用。然后再也没有)(c ++ 17)没有意义。只需定义(及其返回值)“操作符!=”右侧的类型即可。正确吗?
  6. 如何推断“ operator!=”的类型为“ const'ReturnTypeOfEnd'&”。我尝试使用decltype,但失败了。
  7. 显然,“ operator *”的返回类型定义了range_declaration的类型。在我的情况下,“ * __ begin”与“ __begin”的类型完全不同。没有一些地址或其他东西。好吧

如果您能对此有所了解,我会很高兴。 。

1 个答案:

答案 0 :(得分:0)

解决方案的主要问题是,您试图以一种非标准的方式进行处理,这存在很多局限性,并且可能会使精通迭代器在STL中的工作原理的专家难以使用。

我将尝试以技术性较低的方式回答大多数问题,这应该有助于理解其在实践中的工作原理。

1)共享类型将导致一些问题,因为某些算法可能需要多个活动迭代器。另外,它不遵守SRP(单一责任原则),因此不是一个好的设计实践。

2)它只需要返回本质上类似于迭代器的行为。

3)或者,如果数据在内存中是连续的,也可能是一个指针。

4)通常,begin函数按值返回迭代器。

5)通常,end函数将迭代器返回到末尾。如果结尾不是真正的位置(例如,输入流或容器的最后一个值是哨兵),它也可能返回哨兵对象。

6)您可以将自己的typedef / aliases放在类中,并在适当时使用它们。

7)运算符*的返回值类型几乎总是与迭代器类型之一不同。

然后是一些评论/建议

  • 在这种情况下,LinkedList是迭代器类型(或者您可以使用包装器)。
  • 例如,如果您希望能够知道大小而不必迭代整个列表,则通常希望容器不只是一个迭代器。此外,容器可能会提供一些优化的成员函数,例如sort中的std::list
  • 如果您想了解专家的工作方式,则STL源代码可能是一个很好的来源。
  • 您的代码不遵守constness,因此如果将Example example;替换为const Example example;,将无法正常工作。
  • 您的大多数操作员在其声明中都没有遵循常规约定。
  • 您的begin函数有副作用。
  • 使用代码,您将无法将迭代器存储到列表中的某个位置。这意味着排序或删除匹配项的算法可能会失败。
  • void放入空参数列表实际上是一种过时的方式来编写在C ++中不应再使用的代码。它仅在C语言中有用。
  • Operator ++通常应返回对此的引用。
  • 如果要使用哨兵作为最终值,最好使用自己的类型(可以使用enum值)。否则,它将允许意外的比较像example != 25那样进行编译(在这种情况下,可能会在k的值为25时结束循环),从而使代码更难以理解。
  • 为什么不使用std::forward_list而不是重新发明轮子。
  • 如果您确实需要使用LinkedList,那么STL可能是有关如何正确定义迭代器的宝贵信息。