-2

I have a couple of classes derived from Element. Each of them has a generate() method that returns something. Also, I have a Rep class that is derived from Element, too.

I need to initialize a Rep object with any of Element children and be able to call generate().

#include <string>
#include <vector>
#include <iostream>

class Element{
};

class Any : public Element{
        std::string text;
    public:
        Any(std::string text) : text(text) {};
        char generate(); //returns random symbol
};

class Per : public Element{
        std::string text;
    public:
        Per(std::string text) : text(text) {};
        std::string generate();
};

class Format : public Element{
        int format;
        std::string text;
    public:
        Format(int format, std::string text) : format(format), text(text) {};
        std::string generate();
};

class Rep : public Element{
        int count;
        Element action;
    public:
        Rep(int count, Element action) : count(count), action(action) {};
        void generate(){
            for(int i = 0; i<count; i++)
                std::cout<<action.generate();
        }
};

//i want to do this
int main(){
    Rep a(10, Any("help"));
    a.generate(); //eg. "ehplhppelh"
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
1 1
  • 15
  • 3
  • 1
    And what's your specific question now? You forgot to ask one. You probably want `Element` to be an abstract class defining a pure virtual method `virtual char generate() = 0;` – πάντα ῥεῖ Jan 05 '22 at 18:52
  • But other's classes generate method returns std::string and you cant overload virtual methods as I know – 1 1 Jan 05 '22 at 18:56
  • 3
    Simply not possible with the given design; You've got an `Element` object stored in `Rep` and not one of the subclasses. You can only pass `Any` to the constructor, since the temp `Any` object is assignable to `Element const&`, so the default copy constructor is used for the conversion. Even if you did manage to store a reference to an object in there, the only way to to call the proper function is to use `dynamic_cast` to check for all the possible types and use the result, for a matching type or similar. You could make`Rep`a template though and change the type of`action`to the template param – fabian Jan 05 '22 at 18:59

1 Answers1

1

Rep is slicing the Element it is being given. And Element does not have a generate() method. But even if it did, it would have to be virtual, but you can't overload a virtual method based solely on its return type.

I would suggest making Rep be a template class instead, eg:

template<typename ActionType>
class Rep : public Element{
    int count;
    ActionType action;
public:
    Rep(int count, const ActionType &action) : count(count), action(action) {};
    void generate(){
        for(int i = 0; i < count; ++i)
            std::cout << action.generate();
    }
};
int main(){
    Rep<Any> a(10, "help");
    a.generate();
}

Otherwise, consider giving Element a virtual generate() that returns a std::variant, eg:

#include <string>
#include <vector>
#include <iostream>
#include <variant>

using var_t = std::variant<char, std::string, std::monostate>;

class Element{
public:
    virtual var_t generate() const = 0;
};

class Any : public Element{
    std::string text;
public:
    Any(std::string text) : text(text) {};
    var_t generate() const override; //returns random symbol
};

class Per : public Element{
    std::string text;
public:
    Per(std::string text) : text(text) {};
    var_t generate() const override;
};

class Format : public Element{
    int format;
    std::string text;
public:
    Format(int format, std::string text) : format(format), text(text) {};
    var_t generate() const override;
};

class Rep : public Element{
    int count;
    Element &action;
public:
    Rep(int count, Element& action) : count(count), action(action) {};
    var_t generate() const override {
        for(int i = 0; i < count; ++i) {
            std::visit(
                [](auto&& arg) {
                    using T = std::decay_t<decltype(arg)>;
                    if constexpr (!std::is_same_v<T, std::monostate>)
                        std::cout << args;
                },
                action.generate()
            );
        }
        return std::monostate{};
    }
};
int main(){
    Any a("help");
    Rep r(10, a);
    r.generate();
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770