Перегрузка -> оператор для типов указателей и не указателей - PullRequest
0 голосов
/ 07 ноября 2018

Ради практики я решил реализовать контейнер списков со связанным объектом Iterator. Я раньше не реализовывал итераторы, но часто использовал шаблон. Объект итератора выглядит следующим образом:

template <typename DataType>
struct FNode
{
    DataType Data;
    FNode* Next;
    FNode* Prev;

    FNode(DataType _data, FNode* _next, FNode* _prev)
        : Data(_data)
        , Next(_next)
        , Prev(_prev)
    {
    }

};

template <typename DataType>
struct TListIterator
{
    TListIterator(FNode<DataType>* _Element)
        : Element(_Element)
    {
    }

    FNode<DataType>* Element;

    // Some operators overload ....
    // ....

    DataType operator ->()
    {
        return Element->Data;
    }

Итак, по сути, я хочу, чтобы итератор сохранил указатель узла в моем списке / контейнере. Моя проблема возникает при доступе к данным через итератор. Я хочу иметь возможность просто использовать оператор стрелки для прямого доступа к переменной DataType, содержащейся в узле.

Реализация выше работает нормально, если мой аргумент шаблона является указателем. Однако, если я решу использовать список, в котором хранится тип данных без указателя, оператор стрелки завершится ошибкой, поскольку я больше не возвращаю тип указателя. Само собой разумеется, если я изменю перегрузку оператора, чтобы возвратить указатель как это:

    DataType* operator ->()
    {
        return &Element->Data;
    }

Работает для типов без указателей, но не для типов указателей.

Какую ключевую идею я здесь неправильно понимаю - должен ли мой объект итератора не иметь дело с указателями узлов? Или есть способ перегрузить другой оператор для удобной работы с указателями и без указателей?

Я также замечаю, что обычно сталкиваюсь с подобными проблемами при написании кода шаблона. В большинстве случаев я хотел бы обрабатывать данные по-разному в зависимости от того, является ли он указателем или нет.

EDIT Вот несколько примеров

С учетом структуры:

struct FScanHit
{
    unsigned long TimeStamp;
    uint16_t Distance;
    uint8_t  Angle;
};

Учитывая класс:

class Object
{
    public:
    void Update();
}

Попытка управления списком FScanHit и списком объектов *:

TList<FScanHit> scans;
TList<Object*> objects;

// Add elements to either lists
// ....

// Access the actual data
TListIterator<FScanHit> scanIt = scans.Begin();    // Begin uses the TListIterator constructor shown above to create an iterator whose element is the head node of the list
TListIterator<Object*> objectsIt = objects.Begin();

objectsIt->Update();  // Works, because ->() returns a pointer type Object*
uint8_t dist = scanIt->Distance;  // Error: result of 'operator->()' yields non-pointer result

1 Ответ

0 голосов
/ 07 ноября 2018

Exibit A:

template <class T> T foo() { ... }

foo<Object*>()->Update() проверки типов, а foo<Object>()->Update() - нет, поскольку foo<Object>() не является указателем на объект типа класса - он является объектом типа класса.

Приложение B:

template <class T> T* foo() { ... }

Теперь foo<Object>()->Update() проверки типов, а foo<Object*>()->Update() нет, потому что foo<Object*>() снова не указатель на объект типа класса - это указатель на указатель .

У вас точно такая же проблема с вашим operator->().

Какую версию использовать?

Если вы дополняете вторую версию (возвращая указатель) с помощью operator*(), вы можете использовать objectsIt->Update() или (*objectsIt)->Update(), в зависимости от того, есть у вас указатель или нет. Хм, это выглядит знакомо. Разве у objectsIt нет синтаксиса, напоминающего синтаксис обычного указателя? Ну, это именно то, что должен делать итератор!

...