mardi 3 mars 2020

Polymorphism in C++11 using a container of smart pointers

I was expecting the following code to compile whenever I define VECTOR_OF_SHAPES, but it didn't, and I don't understand why.

The idea is to test polymorphism using a container of smart pointers. I've step into similar answers regarding this topic and they point to the need of move constructors & move assignment operators, and that's why I wrote some code around RULE_OF_5 but it still the 'use of deleted function' error arises.

I've tried the code with g++, clang and cpp.sh, all returning nearly the same output.


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

#define inherits_from            : public

class Shape
{
public:
  Shape() { std::cout << __FUNCTION__ << "()" << std::endl; }
#ifdef RULE_OF_5
  Shape(const Shape&) {}
  Shape(Shape&&) {}
  Shape& operator=(const Shape&) { return *this; }
  Shape& operator=(Shape&&) { return *this; }
#endif

  virtual ~Shape() { std::cout << __FUNCTION__ << "()" << std::endl; }

  virtual void Draw() = 0;
};

class Circle inherits_from Shape
{
public:
  virtual void Draw() { std::cout << "Drawing a circle" << std::endl; }
};

class Triangle inherits_from Shape
{
public:
  virtual void Draw() { std::cout << "Drawing a triangle" << std::endl; }
};

class Square inherits_from Shape
{
public:
  virtual void Draw() { std::cout << "Drawing a square" << std::endl; }
};

void test()
{
  auto shape0                   = std::make_unique<Circle>();
  std::unique_ptr<Shape> shape1 = std::make_unique<Triangle>();
  auto shapes                   = std::vector<std::unique_ptr<Shape>>();
#ifdef VECTOR_OF_SHAPES
  auto shapes2                  = std::vector<std::unique_ptr<Shape>>
  {
    std::make_unique<Circle>()
  }; // <- error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Shape; _Dp = std::default_delete<Shape>]’...

#endif
}

int main()
{
  test();
  return 0;
}

Compilation command:

g++-8 -DVECTOR_OF_SHAPES -DRULE_OF_5 -ggdb -O0 -std=c++14 -Wall -Wextra -fPIC -pedantic  polymorphic-test.cpp -o polymorphic-test

Full compiler output:

In file included from /usr/include/c++/8/memory:64,
                 from polymorphic-test.cpp:3:
/usr/include/c++/8/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::unique_ptr<Shape>; _Args = {const std::unique_ptr<Shape, std::default_delete<Shape> >&}]’:
/usr/include/c++/8/bits/stl_uninitialized.h:83:18:   required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::unique_ptr<Shape>*; _ForwardIterator = std::unique_ptr<Shape>*; bool _TrivialValueTypes = false]’
/usr/include/c++/8/bits/stl_uninitialized.h:134:15:   required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::unique_ptr<Shape>*; _ForwardIterator = std::unique_ptr<Shape>*]’
/usr/include/c++/8/bits/stl_uninitialized.h:289:37:   required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const std::unique_ptr<Shape>*; _ForwardIterator = std::unique_ptr<Shape>*; _Tp = std::unique_ptr<Shape>]’
/usr/include/c++/8/bits/stl_vector.h:1469:33:   required from ‘void std::vector<_Tp, _Alloc>::_M_range_initialize(_ForwardIterator, _ForwardIterator, std::forward_iterator_tag) [with _ForwardIterator = const std::unique_ptr<Shape>*; _Tp = std::unique_ptr<Shape>; _Alloc = std::allocator<std::unique_ptr<Shape> >]’
/usr/include/c++/8/bits/stl_vector.h:519:2:   required from ‘std::vector<_Tp, _Alloc>::vector(std::initializer_list<_Tp>, const allocator_type&) [with _Tp = std::unique_ptr<Shape>; _Alloc = std::allocator<std::unique_ptr<Shape> >; std::vector<_Tp, _Alloc>::allocator_type = std::allocator<std::unique_ptr<Shape> >]’
polymorphic-test.cpp:51:3:   required from here
/usr/include/c++/8/bits/stl_construct.h:75:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Shape; _Dp = std::default_delete<Shape>]’
     { ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/8/memory:80,
                 from polymorphic-test.cpp:3:
/usr/include/c++/8/bits/unique_ptr.h:394:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^~~~~~~~~~

So, can anyone explain why whenever I define VECTOR_OF_SHAPES, the compilation fails and how to fix the code? Thanks in advance

Note: My idea of answer would be to find a way of constructing the vector with an initializer list, and not to use push_back() or emplace_back()

Aucun commentaire:

Enregistrer un commentaire