7

Below is the minimalist problem of the code:

struct B { 
  B () = default;
  //~B () {};  // error: use of deleted function ‘B& B::operator=(const B&)’
  std::unique_ptr<int> m_pB = nullptr;
};

int main ()
{
  std::vector<B> vB; 
  vB.erase(vB.begin());
}

Above code compiles fine, unless the destructor is uncommented. For my requirement, I need to have a body of ~B() explicitly defined.

How can I define the body of destructor with the unique_ptr co-existing in the same class?

Note: Tried defining = default versions of copy & move constructor to no avail. In my real code, unique_ptr<int> is unique_ptr<forward_declared_class>. Couldn't locate this problem in SO, though I am sure it must be present. Feel free to mark as dupe.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • Being forward declared is relevant to the problem - can't call `delete` reliably (i.e. can't instantiate `std::default_deleter`) without a visible destructor. – milleniumbug Nov 17 '16 at 12:42
  • See this question http://stackoverflow.com/questions/6012157/is-stdunique-ptrt-required-to-know-the-full-definition-of-t – milleniumbug Nov 17 '16 at 12:47
  • There is no reason to assign nullptr to m_pB. –  Nov 17 '16 at 14:16

3 Answers3

5

Seems like your code requires B to be copy constructible and copy assignable for std::vector (at least for visual c++, which I tested).

Copy constructor and copy assignement operators can only be = delete because of std::unique_ptr (implementations declared = default should cause the function to be deleted as well), and, by implementing destructor, you disable default implementations of move constructor and move assignment operator.

So you need to explicitly declare the move assignment operator. Try:

#include <memory>
#include <vector>

struct B { 
  B () = default;
  ~B () {}
  B& operator=(B&&) = default;
  std::unique_ptr<int> m_pB = nullptr;
};

int main ()
{
  std::vector<B> vB; 
  vB.erase(vB.begin());
}
rocambille
  • 15,398
  • 12
  • 50
  • 68
3

You might declare and implement a move-assignment operator, which the compiler asks for. Of course, it complains about copy assignment, but a sensible copy seems to be impossible for your structure.

Probably, the error message is a bit misleading because std::vector::erase implementation contains something like _data[i-1] = _data[i] in the code which pokes vector elements to places of deleted items. So the compiler needs any assignment operator, and we provide it with a moving one.

For example, this works fine (gcc 4.8.3):

struct B { 
  B () = default;
  B& operator=(B&& op) {
    m_pB = std::move(op.m_pB);
    return *this;
  }
  ~B () {};  // no more errors.
  std::unique_ptr<int> m_pB = nullptr;
};

int main ()
{
  std::vector<B> vB; 
  vB.erase(vB.begin());
}
Sergey
  • 7,985
  • 4
  • 48
  • 80
0

The vector code is a red herring.

When a class U has a member of type unique_ptr, and T is forward declared, the definition of the destructor has to be in the cpp file, when U is no longer an incomplete type.

You should have ~U(); in the header, and in the cpp file have U::~U(){} or U::~U() = default;