operator= не вызывается при присваивании. C++

Я уже попробовал приактически всё, чтобы оператор присваивания вызывался, но этого не происходит. Вот код: Shared.hpp:

#pragma once
namespace atl::util {
    template <class T>
    class shared {
    private:
        void _delete() {
            if (this->m_count == nullptr)
                return;

            (*this->m_count)--;

            if (*this->m_count == 0) {
                if (this->m_value != nullptr)
                    delete this->m_value;
                delete this->m_count;
            }
        }

    protected:
        T* m_value;
        size_t* m_count;

    public:
        shared() : m_value(nullptr), m_count(new size_t(0)) {}
        shared(T* _value, size_t* _count = new size_t(0)) : m_value(_value), m_count(_count) {
            if (m_value != nullptr)
                (*this->m_count)++;
        }
        shared(const shared& _another) : 
        m_value(_another.m_value), m_count(_another.m_count) {
            if (this->m_value != nullptr)
                (*this->m_count)++;
        }
        shared(shared&& _what) noexcept :
        m_value(_what.m_value), m_count(_what.m_count) {
            _what.m_value = nullptr;
            _what.m_count = nullptr;
        }
        ~shared() {
            this->_delete();
        }

        static shared <T> create(T* _value, size_t* _count = new size_t(0)) {
            return shared <T>(_value, _count);
        }

        shared& operator=(const shared& _another);
        shared& operator=(shared&& _what) noexcept;

        T* raw() const {
            return this->m_value;
        }
        T* operator->() const {
            return this->raw();
        }
        T& get() const {
            return *this->m_value;
        }
        T& operator*() const {
            return this->get();
        }

        size_t count() const {
            return *this->m_count;
        }
    };

    template <class T>
    shared <T>& shared <T>::operator=(const shared <T>& _another) {
        if (this == &_another)
            return *this;

        std::cout << "shared <T>::operator= called!\n";
        
        this->_delete();

        this->m_value = _another.m_value;
        this->m_count = _another.m_count;

        if (this->m_value != nullptr)
            (*this->m_count)++;

        return *this;
    }
    template <class T>
    shared <T>& shared <T>::operator=(shared <T>&& _what) noexcept {
        this->_delete();

        this->m_value = _what.m_value;
        this->m_count = _what.m_count;

        _what.m_value = nullptr;
        _what.m_count = nullptr;

        return *this;
    }

    //TODO: const and reinterpreted cast
    template <class T, class U>
    shared <T> static_pointer_cast(const shared <U>& _object) noexcept {
        return shared(static_cast <T*>(_object.raw()));
    }
    template <class T, class U>
    shared <T> dynamic_pointer_cast(shared <U>& _object) noexcept {
        T* temp = dynamic_cast <T*>(_object.raw());
        if (temp)
            return shared <T>(temp);
        else
            return shared <T>();
    }
}

main.cpp:

#include <iostream>
#include <atl/util/Shared.hpp>

using namespace atl::util;

class A {
public:
    virtual void print(char def = '\n') const {
        std::cout << "A" << def;
    }
};

class B : virtual public A {
public:
    void print(char def = '\n') const final override {
        A::print('-');
        std::cout << "B" << def;
    }
};

int main() {
    shared <B> b = shared <B>(new B());
    shared <A> a = dynamic_pointer_cast <A>(b);

    a->print();

    return 0;
}

Куда и как нужно вставить метод присваивания, чтобы он вызывался и выводил в консоль сообщение (которое сделано как костыль, просто чтобы проверить)?

Заранее спасибо за помощь и ответы.


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

Автор решения: VladimirS

Ну, Вы честно вызываете 2 раза конструктор копирования, вместо оператора присваивания. Соответственно, оператор присваивания и не вызывается (вместо него срабатывает конструктор копирования):

    shared <B> b = shared <B>(new B());         // Вызывается shared(const shared&). а не operator=
    shared <A> a = dynamic_pointer_cast <A>(b); // Тоже вызывается shared(const shared&). а не operator=

Компилятор делает в точности то, о чём Вы его и попросили.

Если Вам нужен именно оператор присваивания, то, попробуйте следующее:

  1. конструктор копирования (и перемещения) объявить или =delete, или explicit, т.е.:
   explicit shared(const shared& _another) : 
        m_value(_another.m_value), m_count(_another.m_count) {
            if (this->m_value != nullptr)
                (*this->m_count)++;
        }
   explicit shared(shared&& _what) noexcept :
        m_value(_what.m_value), m_count(_what.m_count) {
            _what.m_value = nullptr;
            _what.m_count = nullptr;
        }

Или, если конструкторы Вам не нужны, можно их "убить" (в C++11 и выше; или поместить в секцию private, если C++98/03):

   shared(const shared& _another) = delete;
   shared(shared&& _what) noexcept = delete;
  1. Если после п.1. компилятор не выдаст Вам ошибку на Ваш вот этот код:
    shared <B> b = shared <B>(new B());
    shared <A> a = dynamic_pointer_cast <A>(b);

(А он должен, после п.1. выдать вам, что - в зависимости от того, что Вы выберете и сделаете в п.1, сообщить, что так делать нельзя, т.к. (любой из вариантов):

  • Конструктор копирования объявлен explicit, следовательно его надо вызывать явно, т.е. так:
    shared <B> b(new B());
    shared <A> a(dynamic_pointer_cast <A>(b));  
  • Конструктор копирования, объявленный =delete, удалён и его вызывать нельзя
  • Конструктор копирования объявлен с модификатором доступа private и его вызывать нельзя

Соответственно, компилятор заставит Вас написать правильный код, в котором точно будет вызываться оператор присваивания, т.е. такой код:

    shared <B> b;//1) Сконструировали b и a
    shared <A> a;
    b = new B(); //2) Присваиваем b и a соответствующие значения
    a = dynamic_pointer_cast <A>(b);  

По логике же Вашего кода в вопросе, оператор присваивания должен был бы вызваться ещё ДО срабатывания конструктора (но по правилам C++ объект сначала конструируется и лишь потом можно вызывать его методы и операторы).

→ Ссылка