Шаблонные структуры с наследованием и переопределенными методами

99
18 марта 2022, 00:00

Есть две шаблонные структуры List и SinglyList.

В самой структуре есть внутренняя структура Node(тк мне нужно чтобы о структуре Node знали только List и его наследники)

Так же есть функция push_back(в будущем будет еще парочку).

И в каждом классе наследнике структура Node, функция push_back, и функция getNode будет разной.

Вот пример List.h

#pragma once
#ifndef LIST_H
#define LIST_H
template<typename T>
struct List {
protected:
    struct Node;
private
    virtual Node* getNode(const int index) = 0;
    int p_size;
public:
    int size() { return p_size; }
    virtual void push_back(const T data) = 0;
    T& operator[](const int index) {
        return getNode(index)->data;
    }
};
#endif //LIST_H

И SinglyList.h

#pragma once
#ifndef SINGLYLIST_H
#define SINGLYLIST_H
#include "List.h"
template<typename T>
struct SinglyList : public List<T> {
public:
    List<T>::Node* head;
    struct List<T>::Node{
        Node() {
            cout << "constructor" << endl;
        }
    }
    List<T>::Node* getNode(const int index) override {
        List<T>::Node* prev = new List<T>::Node(10);
        return prev;
    }
    void push_back(const T data) {
        cout << "push_back method" << endl;
    }
};
#endif //SINGLYLIST_H

Так же в будущем будет структура DoublyList

В которой будет переменная Node* head; и Node* tail; и так же другой конструктор с функциями.

Тот код, который я привел очевидно не работает. Подскажите пожалуйста, как реализовать то что мне нужно. Желательно закрепить свой ответ простым кодом, так же можно посоветовать сайт, на котором я могу подробней почитать об этой теме.

Мне все это нужно чтобы я мог передавать в функцию передавать List и на выходе получать например vector или массив. Что-то типа функции toArray или toVector а эта функция будет у всех структур наследников одинаковая.

Дополнение:

Вот примерно так я представляю структуру.

Как видно по картинке в структуре есть похожие структуры Node, переменные p_size(на картинке я забыл ее добавить) и функции на пример getNode.

Вот я и захотел вынести все похожее в абстрактную структуру.

А насчет toVector я представлял себе это так. Есть функция

vector<T> toVector(List<T> list){
    vector<T> vec;
    for(int i = 0; i < list.size(); ++i)
        vec.push_back(list[i]);
    return vec;
}
Answer 1

Класс(структура), это некая инструкция компилятору о том, какими должны быть экземпляры этого типа и что они могуть сделать. Если в классе вы объявили о новом типе сущности( Node), то вы должны определять что она из себя представляет, но не в другой сущности(в данном случаи в наследнике). Класс(структура) не является пространством имен, чтобы взять и в любом другом классе(структуре) определить тип, принадлежащий другому типу. Нужно написать хоть какое то определение Node в абстрактном классе(можно с пустым телом), а в производном классе написать(если хотите) производную от него структуру. Также нельзя оставлять неинициализированным члены (в данном случаи p_size). Например, можно так:

template<typename T>
struct List {
protected:
    struct Node {
        T data{};
    };
    virtual Node* getNode(const int index) = 0;
private:   
    int p_size{};
public:
    int size() { return p_size; }
    virtual void push_back(const T data) = 0;
    T& operator[](const int index) {
        return getNode(index)->data;
        p_size = index;
    }
    virtual ~List() {};
};
template<typename T>
struct SinglyList : public List<T> {
public:
    struct Node : List<T>::Node {
        Node() {            
            cout << "constructor" << endl;
        }
    };
    //это уже SinglyList::Node
    Node* getNode(const int index) override {
        return new Node[index];        
    }
    void push_back(const T data) {
        cout << "push_back method" << endl;
    }
};

Но, это не то, что вам нужно, и то, что вы написали, это какая то смесь, из чего строить что то осмысленное, пока невозможно. Для получения желаемого результата, методы класса должны иметь определенную смысль, члены класса должны инициализироваться определенным значением. Так что доработайте классы. Также подумайте о смысле этой иерархии, потому что, для окончательной цели(передать один контейнер, а на выходе иметь другой), вам не нужно прибегнуть к наследованию, тем самым увеличивая и время выполнения и размеры обьектов. Пусть ваша функция сама будет шаблоном, который принимает один контейнер, копирует данные в другой контейнер, и вернет его, где параметры шаблона есть типы этих контейнеров.

Answer 2

Варианты решения

  1. Обычный полиморфизм
     template<typename T>
     struct SingleDirectionNode
     {
         T Data;
         SingleDirectionNode * Next;
         bool moveForward();
     };
     template<typename T>
     struct DoubleDirectionNode: SingleDirectionNode<T>
     {
        DoubleDirectionNode * Previouse;
        bool modeBackward();
     };
  1. Статический полиморфизм
     template<typename T>
     struct SingleDirectionNode
     {
         T Data;
         SingleDirectionNode * Next;
     };
     template<typename T>
     struct DoubleDirectionNode
     {
        T Data;
        SingleDirectionNode * Next;
        DoubleDirectionNode * Previouse;
     }
     template<typename N>
     bool Forward(N *& node)
     {
         node = node ? node->Next : nullptr;
         return nullptr != node;
     }
     template<typename N>
     bool Backward(N *& node)
     {
         node = node ? node->Previouse : nullptr;
         return nullptr != node;
     }
READ ALSO
Правила вывода типа для ключевого слова auto

Правила вывода типа для ключевого слова auto

Как определяется тип инициализируемой переменной при использовании ключевого слова auto?

112
Есть ли разница между GCC(g++) и Visual Studio?

Есть ли разница между GCC(g++) и Visual Studio?

Есть пару вопросов от начинающего

146
Указатели, delete и nullptr

Указатели, delete и nullptr

Сап! Объясните максимально подробно, пожалуйста, что именно делают delete и nullptr для указателейВот код:

160
Что за стиль записи в структуре

Что за стиль записи в структуре

Так вот я не понимаю,зачем писать WNDCLASSEX после обЪявления структуры,вычитал где то что это создание объекта,то есть ,будто бы мы объявили WNDCLASSEX...

153