Как правильно определить операции, что компилятор не уходил в warning?

Подскажите, пожалуйста, как можно поправить такую ошибку(да, на самом деле это варнинг, но все же)? Суть в том , что создал свой класс, для него определил операции, в качестве результат операций возвращаю объект класса, который был создан внутри. Ниже привожу ошибку, оператор присваивания, операции и определение класса, привел реализацию функций, которые могут повлиять на появление данной ошибки. Не совсем понимаю,в чем именно проблема.

Ошибка

functions.cpp: In member function 'IntPlaneSet IntPlaneSet::operator^(const IntPlaneSet&)':
                                       
14:06functions.cpp:58:12: error: implicitly-declared 'constexpr IntPlaneSet::IntPlaneSet(const IntPlaneSet&)' is deprecated [-Werror=deprec ated-copy]                                                                                                                                58 |     return r;                                                                                                                        |            ^                                                                                                                   functions.cpp:30:14: note: because 'IntPlaneSet' has user-provided 'IntPlaneSet& IntPlaneSet::operator=(const IntPlaneSet&)'              30 | IntPlaneSet& IntPlaneSet::operator=(const IntPlaneSet& a)                                                                            |              ^~~~~~~~~~~                                                                                                                                        

Оператор присваивания

IntPlaneSet& IntPlaneSet::operator=(const IntPlaneSet& a)
{
    SetZero();
    CopyOnly(a);
    return *this;

}

Операция

IntPlaneSet IntPlaneSet::operator^(const IntPlaneSet& a)
{
    IntPlaneSet r = IntPlaneSet(max(this->x1,a.x1),max(this->x2,a.x2),max(this->y1,a.y1),max(this->y2,a.y2));
    r.b_s = this->b_s ^ a.b_s;
    return r;
}

Так определен класс

#pragma once
#include <cstdio>
#include <cmath>
#include <bitset>
#include <iostream>
#include <cstdlib>
#include <random>
#define x_1 -10
#define y_1 -10
#define x_2 -10
#define y_2 10
#define x_3 10 //допустимые границы, в которых можно задать множество задаются жестко в начале программы
#define y_3 -10
#define x_4 10
#define y_4 10
#define x_size x_3 - x_1 + 1
#define y_size y_2 - y_1 + 1
#define SIZE (x_size)*(y_size)//размер bitset
#define TEST 2 //число массивов в тесте

using namespace std;

class IntPlaneSet
{
    int x1,x2,y1,y2;
    public:

        bitset<SIZE> b_s;
        
        class iterator
        {
            int cur;
            IntPlaneSet* S;
            public:
                iterator(IntPlaneSet *a= NULL)
                {
                    if (a!=NULL)
                    {
                        this->cur = a->FirstNonZero();
                        this->S = a;
                    }
                    else
                    {
                        this->cur = -1;
                        this->S=NULL;
                    }

                }

                int operator!=(const iterator &b);
                
                int operator==(const iterator &b);

                void operator++();


                void operator++(int);
                
                pair<int, int> operator*();

        };

        IntPlaneSet(int x1=x_1,int x2=x_3,int y1=y_1, int y2=y_2,int rand_key = 0 )
        {
            this->x1=x1;
            this->x2=x2;
            this->y1=y1;
            this->y2=y2;
            this->b_s=( 0 );
            if (rand_key == 1)
            {
                std::random_device rd;
                std::mt19937 rng(rd());
                std::uniform_int_distribution<int> ValsX(x1,x2);
                std::uniform_int_distribution<int> ValsY(y1,y2);
                for(int i = 0;i<abs(x2-x1)*abs(y2-y1);i++)
                {
                    int x=ValsX(rng);
                    int y = ValsY(rng);
                    int index =  ((y - y_1)*((x_3 - x_1)+1)) + ((x - x_1));
                    this->b_s[index]=1;
                }

            }


        }
        ~IntPlaneSet()
        {
            this->SetZero();
        }
        void SetZero()
        {
            this->b_s = ( 0 );
        }
        
        pair<int, int> GetValue(int a);

        void AddValue(const int x,const int y);

        void DeleteValue(const int x,const int y);

        void Print()
        {
            int index=0;
            cout << "Bitset : ( "<< this->b_s << " )\n"<< endl;
            
            for(int i=y_2;i>y_1-1;i--)
            {
                for (int j=x_1;j<x_3+1;j++)
                {
                    index =  ((i - y_1)*((x_3 - x_1)+1)) + ((j - x_1));
                    cout << this->b_s[index] << " ";
                }
                printf("\n");
                
            }



        }

        void Check(const int x,const int y);

        int* Borders();

        void ChangeBorders(int x1, int x2, int y1, int y2);

        void ChangeBorders(int* a);
        
        int** MinMax();

        IntPlaneSet operator&(const IntPlaneSet& a);

        IntPlaneSet operator|(const IntPlaneSet& a);

        IntPlaneSet operator^(const IntPlaneSet& a);

        IntPlaneSet operator~();

        void CopyOnly(const IntPlaneSet& a);

        IntPlaneSet& operator=(const IntPlaneSet& a);

        int FirstNonZero();

        int NextNonZero(int a);

        iterator begin()
        {
            return iterator(this);
        }

        iterator end()
        {
            return iterator(NULL);
        }
};

Ответы (2 шт):

Автор решения: Qwertiy
IntPlaneSet r = IntPlaneSet(max(this->x1,a.x1),max(this->x2,a.x2),max(this->y1,a.y1),max(this->y2,a.y2));
IntPlaneSet r(max(this->x1,a.x1),max(this->x2,a.x2),max(this->y1,a.y1),max(this->y2,a.y2));
→ Ссылка
Автор решения: HolyBlackCat

Вы написали для своего класса деструктор и копирующий оператор присваивания, но не написали копирующий конструктор, и перемещающие конструктор и оператор присваивания.

Поэтому... (см. ряды "destructor" и "copy assignment")

pic
(за таблицу спасибо Говарду Хиннанту)

Поэтому компилятор не стал генерировать перемещающие операции (5,6 колонки).

Но копирующий конструктор сгенерировал (3 колонка), хотя это поведение считается устаревшим (deprecated), начиная с C++11.

А вы где-то использовали этот копирующий конструктор, поэтому компилятор вас и предупреждает, что надеяться на его наличие не стоит.

Решение - убрать самодельный деструктор и копирующее присваивание. Все равно они ничего полезного не делают.


Если бы в них было что-то полезное, то есть другой вариант: явно попросить компилятор сгенерировать недостающие операции

IntPlaneSet(const IntPlaneSet &) = default;
IntPlaneSet(IntPlaneSet &&) = default;
// IntPlaneSet &operator=(const IntPlaneSet &) = default;
IntPlaneSet &operator=(IntPlaneSet &&) = default;
// ~IntPlaneSet() = default;

Советую почитать про правило трех/пяти/нуля.

Народная мудрость гласит, что у класса должны быть, на выбор:

  • Деструктор и копирующие операции (всего 3)
  • Деструктор и копирующие и перемещающие операции (всего 5)
  • Ничего из пяти - самый распространенный вариант (0)

А если комбинация какая-то другая, то это почти всегда ошибка, как в вашем случае.

→ Ссылка