Диапазон, основанный на C ++ 17 для пользовательского контейнера или общих классов с различными типами начала / конца - PullRequest
0 голосов
/ 24 декабря 2018

Я использую g ++ v8.2 с -std = c ++ 2a -Wall- -Wextra -pedantic, работающей на Raspberry Pi 3B +.Я пытаюсь лучше понять диапазон, основанный на цикле для моих пользовательских контейнеров.

Ниже вы видите класс, который реализует пользовательский класс с ранжированным основанием для.Это работает нормально.Тем не мение.У меня есть некоторые дополнительные конкретные вопросы к моей рабочей реализации.

Я уже проверил также это и другие.Но никто из них не отвечает на мои вопросы.

Вот код:

#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. Функция 'end' (я проверял: она вызывается сразу после 'begin'. Затем никогда больше) не имеет смысла (c ++ 17).Он просто для определения (с его возвращаемым значением) типа для правой части оператора «=!».Правильно?
  6. Как я могу вывести тип для оператора '=!, Который будет "const" ReturnTypeOfEnd' & ".Я попытался с decltype, но мне это не удалось.
  7. Очевидно, что тип возвращаемого значения 'operator *' определяет тип range_declaration.В моем случае «* __ begin» имеет совершенно другой тип, чем «__begin».Не какой-то адрес или что-то.Хорошо?

Я был бы счастлив, если бы вы могли пролить свет на это.,.

1 Ответ

0 голосов
/ 24 декабря 2018

Основная проблема вашего решения заключается в том, что вы пытаетесь сделать это нестандартным способом, который имеет много ограничений и может затруднить использование экспертом, который хорошо понимает, как итераторы работают в 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 может стать ценным источником информации о том, как правильно определять итераторы.
...