Добавить в стандартную библиотеку intrusive_ptr

kotbegemot
kotbegemot

template<class T>
    class intrusive_ptr {
    public:
        using pointer = T*;
        using const_pointer = const T*;
        using element_type = T;
        using reference = T&;
        using const_reference = const T&;

        constexpr intrusive_ptr() noexcept : ptr_(nullptr) {}

        intrusive_ptr(pointer raw_ptr, bool add_ref = true) noexcept {
            set_ptr(raw_ptr, add_ref);
        }

        intrusive_ptr(intrusive_ptr &&other) noexcept : ptr_(other.detach()) {}

        intrusive_ptr(const intrusive_ptr &other) noexcept {
            set_ptr(other.get(), true);
        }

        template<class Y>
        intrusive_ptr(intrusive_ptr<Y> other) : ptr_(other.detach()) {
            static_assert(std::is_convertible<Y *, T *>::value,
                          "Y* is not assignable to T*");
        }

        intrusive_ptr &operator=(pointer ptr) noexcept {
            reset(ptr);
            return *this;
        }

        intrusive_ptr &operator=(intrusive_ptr other) noexcept {
            swap(other);
            return *this;
        }

        ~intrusive_ptr() {
            if (ptr_) {
                intrusive_ptr_release(ptr_);
            }
        }

        pointer detach() noexcept {
            auto result = ptr_;
            if (result) {
                ptr_ = nullptr;
            }
            return result;
        }

        pointer release() noexcept {
            return detach();
        }

        void reset(pointer new_value = nullptr, bool add_ref = true) noexcept {
            auto old = ptr_;
            set_ptr(new_value, add_ref);
            if (old) {
                intrusive_ptr_release(old);
            }
        }

        pointer get() const noexcept {
            return ptr_;
        }

        pointer operator->() const noexcept {
            return ptr_;
        }

        reference operator*() const noexcept {
            return *ptr_;
        }

        bool operator!() const noexcept {
            return ptr_ == nullptr;
        }

        explicit operator bool() const noexcept {
            return ptr_ != nullptr;
        }

        void swap(intrusive_ptr &other) noexcept {
            std::swap(ptr_, other.ptr_);
        }

        template<class C>
        intrusive_ptr<C> downcast() const noexcept {
            return (ptr_) ? dynamic_cast<C *>(get()) : nullptr;
        }

        template<class C>
        intrusive_ptr<C> upcast() const noexcept {
            return (ptr_) ? static_cast<C *>(get()) : nullptr;
        }

    private:
        inline void set_ptr(pointer raw_ptr, bool add_ref) noexcept {
            ptr_ = raw_ptr;
            if (raw_ptr && add_ref) {
                intrusive_ptr_add_ref(raw_ptr);
            }
        }

        pointer ptr_;
};

6
рейтинг
6 комментариев
yndx-antoshkka

Идея мне нравится. Есть придирки:
* operator!() не нужен
* downcast() и upcast() надо заменить на *_pointer_cast
* Возможно что будет лучше, если intrusive_ptr_release и intrusive_ptr_add_ref  будут принимать ссылки, а не указатели (это упростит жизнь пользователям, им не придётся гадать "А может ли мне прийти nullptr?").
* Конструкторы должны быть от шаблонных параметров, а не от T. Это спасёт например от implicit deduction guides (которые тут могут навредить). См http://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
* operator* не должен быть noexcept (разыменовывание nullptr ведёт к UB, при UB noexcept не пишется)

Отдельно стоит расписать, почему свободные функции используются для кастомизации (а не какой-то шаблонный параметр). И добавить требование, что функции intrusive_ptr_release и intrusive_ptr_add_ref должны быть noexcept, иначе - ошибка компиляции.

yndx-antoshkka
languagelawyer

> при UB noexcept не пишется

Откуда такое правило?

languagelawyer
yndx-antoshkka
в некоторых компиляторах есть различные флаги, позволяющие в случае UB например кидать исключение. Если помечать функции с UB как noexcept, то вместо исключения будет вызван std::terminate.
yndx-antoshkka
languagelawyer

yndx-antoshkka, noexcept у std::shared_ptr::operator* - это дефект?

languagelawyer
Обновлено 
yndx-antoshkka

languagelawyer, да. К несчастью не устранимый https://cplusplus.github.io/LWG/issue2337

Для консистентности думают сделать правки в другие части стандартной библиотеки https://cplusplus.github.io/LWG/issue2762 но пока что их не приняли. Так что лучше пока не добавлять noexcept по аналогии с большинством operator*()

yndx-antoshkka
neondev9

поддерживаю

neondev9
Другие идеи
Группа создана, чтобы собирать предложения к стандарту C++, организовывать их внутренние обсуждения, помогать готовить их для отправки в комитет и защищать на общих собраниях в рабочей группе по С++ Международной организации по стандартизации (ISO).
Все предложения