3
#include <iostream>
#include <memory>
#include <vector>
#include <utility>
#include <map>

class IOutput{
public:
    virtual std::ostream& print(std::ostream& out) const = 0;
    virtual std::unique_ptr<IOutput> clone() const = 0;
    virtual ~IOutput() = default;
};

std::ostream& operator<<(std::ostream& out, const IOutput& ser) {
    ser.print(out);
    return out;
}

class String : public IOutput {
private:
    std::string m_str;
public:
    String( std::string str )
        : m_str(std::move(str))
    {}
    std::ostream& print(std::ostream& out) const override {
        out << m_str;
        return out;
    }
    std::unique_ptr<IOutput> clone() const override { return std::make_unique<String>(*this); }
};

class List : public IOutput {
private:
    std::vector<std::unique_ptr<IOutput>> m_elements;
public:
    std::ostream& print(std::ostream& out) const override {

    }
    std::unique_ptr<IOutput> clone() const override {
        return std::make_unique<List>(*this); 
    } 
};




int main () {
    String s {"string s"};
    String s2 {"string s2"};
    String s3 {"string s3"};
    String s4 {"string s4"};


    return 0;
}

Error

/usr/include/c++/7/bits/stl_construct.h:75: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = IOutput; _Dp = std::default_delete<IOutput>]’
 { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

I found out that this error occurs only if I have std::vector<std::unique_ptr<IOutput>> in List . It looks like I am copying that vector when I am calling std::make_unique<List>(*this) in clone , which means it has to copy each element from that vector, which in our case (unique_ptr) is forbidden.
How to solve this?

sliziky
  • 129
  • 2
  • 3
  • 10
  • you can't copy a unique_ptr. See https://stackoverflow.com/questions/16030081/copy-constructor-for-a-class-with-unique-ptr for more information – Ody Dec 13 '18 at 14:59
  • 1
    When cloning a vector of unique pointers, you need to explicitly go over the original vector, clone each element, and push the result to the target vector. – n. m. could be an AI Dec 13 '18 at 15:00

3 Answers3

6

The compiler cannot generate a copy constructor, because the vector member is non-copyable. If you wish to support copying, you need to implement it yourself, for instance:

List(List const& other) : m_elements(other.m_elements.size()) {
    std::transform(begin(other.m_elements), end(other.m_elements), begin(m_elements),
      [](std::unique_ptr<IOutput> const& o_ptr) {
        return o_ptr->clone();
      }
    );
}
StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
1

Alternative solution without std::transform

List(const List& other)
{
    m_elements.reserve(other.m_elements.size());
    for (const auto& x: other.m_elements) {
        m_elements.emplace_back(x->clone());
    }
}
Slava
  • 43,454
  • 1
  • 47
  • 90
sliziky
  • 129
  • 2
  • 3
  • 10
  • Sidenote: the `const` for `x` is *optional* - as elements in the vector are const, `x` will be const anyway (because of being reference!), no matter if explicitly declared as or not... But id does not disturb either. – Aconcagua Dec 13 '18 at 15:25
0

IOutput has a clone() member function. You should make a new List that contains clones of the elements of the vector.

Khouri Giordano
  • 1,426
  • 15
  • 17