Home > database >  copy assignment operator implicitly deleted because field has a deleted copy assignment operator
copy assignment operator implicitly deleted because field has a deleted copy assignment operator

Time:09-16

I'm getting this error as soon as I define a destructor, but without that the compilation succeed, but I badly want to define destructor to debug some seg faults.

class Socket {
    private:
        seastar::output_stream<char> socket;

    public:
        std::string peer;
    public:
        Socket() = default;

        template <typename... Args>
        explicit Socket(seastar::output_stream<char> &&s, std::string p) : socket(std::move(s)), peer(p) {}

        ~Socket() {
            std::cout << "Socket has gone out of scope" << "\n";
        }

        seastar::future<> send(std::string message) {
            co_await socket.write(message);
            co_await socket.flush();
        }

        seastar::future<> close() {
            co_await socket.close();
        }
};

Compilation fails with,

error: object of type 'Socket' cannot be assigned because its copy assignment operator is implicitly deleted
        connection->second.socketObj = std::move(socketObj);
                                  ^
./socketwrapper.h:44:46: note: copy assignment operator of 'Socket' is implicitly deleted because field 'socket' has a deleted copy assignment operator
                seastar::output_stream<char> socket;

Is there anyway to fix this issue?

CodePudding user response:

Add

Socket(Socket&&)=default;
Socket& operator=(Socket&&)=default;

CodePudding user response:

The underlying question here is "Why is the compiler trying to use a copy-assignment when I'm explicitly using std::move()?"

So let's see a simple, well-formed example:

struct MyStruct {
  MyStruct() 
    : some_data(new int(12)) {}

  MyStruct(const MyStruct& other) 
    : some_data(new int(*other.some_data)) {}

  MyStruct& operator=(const MyStruct& rhs) {
    delete some_data;
    some_data = new int(*rhs.some_data);
    return *this;
  }

  ~MyStruct() { 
    delete some_data;
  }

  int * some_data;
};

MyStruct foo() {
  return MyStruct();
}

int main() {
  MyStruct a;
  a = foo(); // <--- copy-assignment here, no choice.
}

It's pretty obvious that's it's important for the copy-assignment to be used, despite it being a RValue scenario.

But why is that program well-formed in the first place? It's a move-assignment setup and I have no move-assignment operator. Can't the compiler just give me a slap on the fingers? The thing is, this was well-formed before C 11 and move-semantics were a thing, so it must keep behaving correctly.

So, in the presence of any other constructors/destructor/assignment-operator, if there is no move assignment/constructor present, the copy assignment/copy-constructor must be used instead, just in case it happens to be code that was written a long time ago.

The immediate fix for you is to add a move-assignment operator. At that point, it might also help to clean things up and complete the full rule-of-5.

class Socket {
    private:
        seastar::output_stream<char> socket;

    public:
        std::string peer;
    public:
        Socket() = default;

        explicit Socket(seastar::output_stream<char> &&s, std::string p) 
          : socket(std::move(s)), peer(p) {}
         
        Socket(Socket&&) = default;
        Socket& operator=(Socket&&) = default;

        // These are technically redundant, but a reader wouldn't know it,
        // so it's nice to be explicit.
        Socket(const Socket&) = delete;
        Socket& operator=(const Socket&) = delete;

// ...
  •  Tags:  
  • c
  • Related