Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why in C++11 or C++14 does the compiler implicitly delete the copy constructor when I declare a move assignment operator?

I wanted to create a list data structure with an iterator class in it. Everything works well but when I declare a move assignment operator the program doesn't compile if it's using the C++14 or C++11 standards, but works fine in C++17, C++2a.

list.h:

#pragma once

#include <iostream>

template <typename T>
class list {
    struct node {
        node(T data, node* prev = nullptr, node* next = nullptr)
            : data{ data }, prev{ prev }, next{ next } {}
        
        T data;
        node* prev;
        node* next;
    };
public:
    struct iterator {
        template <typename>
        friend class list;

        explicit iterator(node *_node = nullptr) 
            : _node(_node) {}

        iterator& operator=(iterator const &it) {
            _node = it._node;
        
            return *this;
        }
       
        iterator& operator=(iterator &&it) {
            // does nothing
            return *this;
        }

        T& operator*() {
            return _node->data;
        }

    private:
        node *_node;
    };

    list(T data) {
        Head = Tail = new node(data);

        size = 1;
    }

    iterator begin() {
        return iterator(Head);
    }
private:
    node* Head;
    node* Tail;

    int size;
};

main.cpp:

#include "list.h"

int main() {

    list<int> lst(100);

    std::cout << *lst.begin() << std::endl;

}

It's a stripped-down version but it's enough to describe the problem.

Compiler error and note message:

error: use of deleted function ‘constexpr list::iterator::iterator(const list::iterator&)’
     return iterator(Head);
                         ^

note: ‘constexpr list::iterator::iterator(const list::iterator&)’ is implicitly declared as
       deleted because ‘list::iterator’ declares a move constructor or move assignment operator
     struct iterator {
        ^~~~~~~~
like image 924
Kanony Avatar asked Aug 30 '20 13:08

Kanony


People also ask

Why do we delete the copy constructor?

When to delete copy constructor and assignment operator? Copy constructor (and assignment) should be defined when ever the implicitly generated one violates any class invariant. It should be defined as deleted when it cannot be written in a way that wouldn't have undesirable or surprising behaviour.

Why do we delete copy constructor in C++?

The copy constructor and copy-assignment operator are public but deleted. It is a compile-time error to define or call a deleted function. The intent is clear to anyone who understands =default and =delete . You don't have to understand the rules for automatic generation of special member functions.

What happens when the copy constructor is declared private?

Explanation: The copy constructor can be defined as private. If we make it private then the objects of the class can't be copied. It can be used when a class used dynamic memory allocation. Explanation: The object should not be modified in the copy constructor.

What is move assignment in C++?

In the C++ programming language, the move assignment operator = is used for transferring a temporary object to an existing object. The move assignment operator, like most C++ operators, can be overloaded. Like the copy assignment operator it is a special member function.


1 Answers

return iterator(Head);

Why this works in C++17 is because in C++17 this is guaranteed copy elision. No copy or move takes place here.

In a return statement, when the operand is a prvalue of the same class type (ignoring cv-qualification) as the function return type:

Prior to C++17, this requires a constructor, but the defined move assignment operator deletes it, it's also deleted in C++17, but copy elision does not require it (ibid):

The copy/move constructors need not be present or accessible

like image 58
Sam Varshavchik Avatar answered Oct 29 '22 01:10

Sam Varshavchik