c++c++11c++17copy-assignmentmove-assignment-operator

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 {
        ^~~~~~~~

Solution

  • 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