Morpheus
Morpheus

Reputation: 3523

How to create a vector of unique pointers of a class

I am looking at a code which uses new and delete to create pointers for classes and I am trying to update the code to use unique pointers. Here is my code.

#include <iostream>
#include <memory>
#include <vector>

class Algorithm
{
    private:
    public:
        virtual void runn_algo() = 0;
        virtual ~Algorithm() {};
};

class Algo1 : public Algorithm
{
        void runn_algo()
        {
                std::cout << "running Algo1" << std::endl;
        }
};

class Algo2 : public Algorithm
{
        void runn_algo()
        {
                std::cout << "running Algo2" << std::endl;
        }
};

class runner
{
    public:
        void runn_algo(std::unique_ptr<Algorithm> upa)
        {
                upa->runn_algo();
        }
};

int main()
{
        std::vector<std::unique_ptr<Algorithm>> algos;

        algos.emplace_back(std::make_unique<Algo1>());
        algos.emplace_back(std::make_unique<Algo1>());
        algos.emplace_back(std::make_unique<Algo2>());

        runner run;

        for (auto i = 0u; i < algos.size(); ++i)
        {
                run.runn_algo(algos[i]);
        }
}

I am getting the following error:

call to implicitly-deleted copy constructor of 'std::unique_ptr<Algorithm>'
                run.runn_algo(algos[i]);

I am not sure why this is happening. the code with raw pointers is working fine

class runner
{
    public:
        void runn_algo(Algorithm* upa)
        {
                upa->runn_algo();
        }
};

int main()
{
        std::vector<Algorithm*> algos;

        algos.push_back(new Algo1());
        algos.push_back(new Algo1());
        algos.push_back(new Algo2());

        runner run;

        for (auto i = 0u; i < algos.size(); ++i)
        {
                run.runn_algo(algos[i]);
        }
}

Upvotes: 0

Views: 136

Answers (2)

Cory Kramer
Cory Kramer

Reputation: 117856

First your emplace_back should actually be a push_back since you are using make_unique

algos.push_back(std::make_unique<Algo1>());
algos.push_back(std::make_unique<Algo1>());
algos.push_back(std::make_unique<Algo2>());

Second you should use virtual and override to make your methods polymorphic

virtual void runn_algo();  // base
void runn_algo() override; // derived

Third you should probably pass the argument as a raw (non-owning) pointer to your runner, because if you try to pass unique_ptr by value, it will try to copy, which is not allowed by a unique_ptr

void runn_algo(std::unique_ptr<Algorithm> alg);  // nope, cannot copy
void runn_algo(Algorithm* alg);                  // ok, non-owning

for (auto i = 0u; i < algos.size(); ++i)
{
    run.runn_algo(algos[i].get());               // access underlying pointer
}

Though in this case, I don't really see the point of the runner as you can just use the polymorphism here

for (auto& alg : algos)
{
    alg->run_algo();  // will be polymorphic
}

Upvotes: 2

Nathan Pierson
Nathan Pierson

Reputation: 5565

As the error message indicates, unique_ptr doesn't have a copy constructor and so cannot be passed by value to a function. You can change the signature to accept a const std::unique_ptr<Algorithm>& reference instead:

class runner
{
public:
    void runn_algo(const std::unique_ptr<Algorithm>& upa)
    {
        upa->runn_algo();
    }
};

Which produces the output

running Algo1
running Algo1
running Algo2

As expected.

Alternatively, write runner::runn_algo in such a way that it doesn't care about how the Algorithm is stored:

class runner
{
public:
    void runn_algo(Algorithm& algo)
    {
        algo.runn_algo();
    }
};

int main()
{
    std::vector<std::unique_ptr<Algorithm>> algos;

    algos.emplace_back(std::make_unique<Algo1>());
    algos.emplace_back(std::make_unique<Algo1>());
    algos.emplace_back(std::make_unique<Algo2>());

    runner run;

    for (auto i = 0u; i < algos.size(); ++i)
    {
        run.runn_algo(*algos[i]); // Dereference operator on algos[i]
    }
}

Upvotes: 4

Related Questions