Search code examples
c++c++11polymorphismshared-ptrsmart-pointers

Vector of shared pointers to templated classes


I have a templated class TaskRunner that takes a polymorphic type Task and I want to create a container of shared pointers to them.

class Task {
    virtual void run() = 0;
};

class LoudTask : Task {
    void run() {
        std::cout << "RUNNING!" << std::endl;
    }
};

class QuietTask : Task {
    void run() {
        std::cout << "running!" << std::endl;
    }
};

template<typename T> class TaskRunner {
    public:
        TaskRunner<T>() {
            task = std::make_unique<T>();
        }
    private:
        std::unique_ptr<T> task;
};


using Runner = std::shared_ptr<TaskRunner<Task>>;

However I get error: no matching member function for call to 'push_back' with:

std::vector<Runner> runners;

runners.push_back(std::make_shared<TaskRunner<QuietTask>>());
runners.push_back(std::make_shared<TaskRunner<LoudTask>>());

Due to:

note: candidate function not viable: no known conversion from 'shared_ptr<TaskRunner>' to 'const shared_ptr<TaskRunner>' for 1st argument


Solution

  • Implemented IgorTandetnik's suggestion, and it works for me:

    #include <iostream>
    
    #include <memory>
    #include <vector>
    
    class Task {
        virtual void run() = 0;
    };
    
    class LoudTask : Task {
    public:
        void run() {
            std::cout << "RUNNING!" << std::endl;
        }
    };
    
    class QuietTask : Task {
    public:
        void run() {
            std::cout << "running!" << std::endl;
        }
    };
    
    class TaskRunnerBase
    {
    public:
        virtual void run() =0;
    };
    
    template <class T>
    class TaskRunner: public TaskRunnerBase {
        public:
            TaskRunner():
                task(std::make_unique<T>()) {
            }
            void run() override
            {
                task->run();
            }
        private:
            std::unique_ptr<T> task;
    };
    
    int main()
    {
        using Runner = std::shared_ptr<TaskRunnerBase>;
        std::vector<Runner> runners;
        
        runners.push_back(std::make_shared<TaskRunner<QuietTask>>());
        runners.push_back(std::make_shared<TaskRunner<LoudTask>>());
    
        runners[0]->run();
        runners[1]->run();
    }
    

    Output:

    running!
    RUNNING!
    

    Note however that TaskRunner doesn't need to be a template; as it is currently implemented above, it has a kind of double role: (1) task factory, and (2) container and runner of tasks.

    paolo's answer separates this out nicely, there, the factory aspect is moved to the main function.