Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ copy constructor: attempting to reference a deleted function

I have a class called classA, something like this:

class classA {
    private:        
        char* data; 
    public:
        classA(const classA&) = delete;
        ~classA();      
    };

~classA()
    {
        delete[] data;
    }

In another class, let's call it classB, I have as a member a shared pointer to classA:

class classB
    {
    private:
        std::shared_ptr<classA> ptrA;
    public: 
        classB(std::shared_ptr<classA>);
    };

classB(std::shared_ptr<classA> sp) : ptrA(sp)
    {}

This is how I instantiate my classB:

classA ca;
classB cb(std::make_shared<classA>(ca));

This gives me the following error:

attempting to reference a deleted function

Obviously, I am trying to reference the copy constructor, which I defined as deleted (there is a reason for this, objects of this class shouldn't be copied). But I am confused as to why the copy constructor is called since I am passing a shared pointer, and how to avoid this.

like image 544
Eutherpy Avatar asked Jan 03 '23 13:01

Eutherpy


2 Answers

You're calling the copy constructor trying to make the shared pointer.

std::make_shared<classA>(ca)
                         ^^ constructs the classA using these parameters

You can call make_shared<classA>() to create a shared pointer to a default constructed classA. Or chose another constructor.

like image 101
kmdreko Avatar answered Jan 22 '23 09:01

kmdreko


This example can be simplified quite a bit.

#include <memory>

class classA {
 public:
  classA(const classA&) = delete;
  classA() = default;
};


int main() {
  classA ca; // note, no ()
  auto sp = std::make_shared<classA>(ca); // problem.
  auto sp2 = std::make_shared<classA>();  // this does what you want
}

You are passing ca as an argument to std::make_shared, which constructs a classA by calling classA::classA with whatever arguments that you passed to make_shared.

This might be more obvious if you consider how a make_shared might be implemented.

template <typename Cls, typename... Args>
std::shared_ptr<Cls> MakeShared(Args&&... args) {
                                     //this is where the copying happens
  return std::shared_ptr<Cls>{new Cls{std::forward<Args>(args)...}};
}

int main() {
  classA ca;
  auto sp = MakeShared<classA>(ca);
  auto sp2 = MakeShared<classA>();
}

You pass ca to MakeShared, which then calls new Cls(...) where the ... is whatever you passed to MakeShared, in this case, another classA object.

If the above is too dense (maybe you aren't used to forward or variadic templates), then consider this simplified version of MakeShared which does the same thing for your problem case

template <typename Cls, typename Arg>
std::shared_ptr<Cls> MakeShared(const Arg& arg) {
                      // copy here ----+
                      //               v
  return std::shared_ptr<Cls>{new Cls{arg}};
}

int main() {
  classA ca;
  auto sp = MakeShared<classA>(ca);
}
like image 27
Ryan Haining Avatar answered Jan 22 '23 11:01

Ryan Haining