This is a redesign of the class system for a project I started a while ago (I posted about it, but it was really a lot of time ago and the codebase changed so much).I rewrote this because the previous system was a complete mess with objects that were forced to be allocated dinamically, void pointers, casts, enums, non-smart pointers... that kind of stuff. So I decided to start reading a good book (Effective C++) and this is what I've managed to write.The code should be pretty straightforward (tell me if it isn't). We have two grand-types of classes: values, which contain a single data attribute, and tokens, which are meant to be convertible to values and contain and additional line member.Both values and tokens need to contain different types of data (doubles or strings, mostly) and that's why I'm manipulating these via a polyphormic base class (Value::Base and Token::Base).Tokens can also be of a type, symbol, which shouldn't be convertible to value.types.hpp:#pragma once#include <string>#include <memory>#include <list>namespace Value {    enum class Type {        number,        variable,        name,        string    };    struct Base {        Value::Type type;        virtual ~Base() = default;        protected:        Base(const Value::Type &&new_type);    };    struct Number: public Value::Base {        double data;        Number(const double &new_data);        ~Number() override = default;    };    struct Variable: public Value::Base {        std::string data;        Variable(const std::string &new_data);        ~Variable() override = default;    };    struct Name: public Value::Base {        std::string data;        Name(const std::string &new_data);        ~Name() override = default;    };    struct String: public Value::Base {        std::string data;        String(const std::string &new_data);        ~String() override = default;    };    using Ptr = std::unique_ptr<Value::Base>;    using List = std::list<Value::Ptr>;};namespace Token {    enum class Type {        number,        variable,        name,        string,        symbol    };    struct Base {        Token::Type type;        const unsigned int line;        virtual ~Base() = default;        virtual Value::Ptr to_value() = 0;        protected:        Base(const Token::Type &&new_type, const unsigned int &new_line);    };    struct Number: public Token::Base {        double data;        Number(const double &new_data, const unsigned int &new_line);        ~Number() override = default;        Value::Ptr to_value() override;    };    struct Variable: public Token::Base {        std::string data;        Variable(const std::string &new_data, const unsigned int &new_line);        ~Variable() override = default;        Value::Ptr to_value() override;    };    struct Name: public Token::Base {        std::string data;        Name(const std::string &new_data, const unsigned int &new_line);        ~Name() override = default;        Value::Ptr to_value() override;    };    struct String: public Token::Base {        std::string data;        String(const std::string &new_data, const unsigned int &new_line);        ~String() override = default;        Value::Ptr to_value() override;    };    struct Symbol: public Token::Base {        char data;        Symbol(const char &new_data, const unsigned int &new_line);        ~Symbol() override = default;        Value::Ptr to_value() override = 0;    };    using Ptr = std::unique_ptr<Token::Base>;    using List = std::list<Token::Ptr>;};types.cpp:#include ../include/types.hpp#include <string>Value::Base::Base(const Value::Type &&new_type):    type(new_type) {}Value::Number::Number(const double &new_data):    Value::Base(Value::Type::number), data(new_data) {}Value::Variable::Variable(const std::string &new_data):    Value::Base(Value::Type::variable), data(new_data) {}Value::Name::Name(const std::string &new_data):    Value::Base(Value::Type::name), data(new_data) {}Value::String::String(const std::string &new_data):    Value::Base(Value::Type::string), data(new_data) {}Token::Base::Base(const Token::Type &&new_type, const unsigned int &new_line):    type(new_type), line(new_line) {}Token::Number::Number(const double &new_data, const unsigned int &new_line):    Token::Base(Token::Type::number, new_line), data(new_data) {}Value::Ptr Token::Number::to_value() {    return Value::Ptr(new Value::Number(data));}Token::Variable::Variable(const std::string &new_data, const unsigned int &new_line):    Token::Base(Token::Type::variable, new_line), data(new_data) {}Value::Ptr Token::Variable::to_value() {    return Value::Ptr(new Value::Variable(data));}Token::Name::Name(const std::string &new_data, const unsigned int &new_line):    Token::Base(Token::Type::name, new_line), data(new_data) {}Value::Ptr Token::Name::to_value() {    return Value::Ptr(new Value::Name(data));}Token::String::String(const std::string &new_data, const unsigned int &new_line):    Token::Base(Token::Type::string, new_line), data(new_data) {}Value::Ptr Token::String::to_value() {    return Value::Ptr(new Value::String(data));}Token::Symbol::Symbol(const char &new_data, const unsigned int &new_line):    Token::Base(Token::Type::symbol, new_line), data(new_data) {}I especially need advice on polyphormy, virtual functions and the like because this is the first time I use these features, but don't be afraid of pointing out whatever advice on different subjects to make my code better.
Redesign of classes for my programming language's interpreter
c++;object oriented;c++11