Попытка «реплицировать» поведение upcasting shared_ptr приводит к бесконечной рекурсии в конструкторе копирования (что приводит к segfault) - PullRequest
1 голос
/ 05 июня 2019

Я пытаюсь повторить поведение shared_ptr, чтобы лучше понять метапрограммирование шаблонов и type_traits в c ++ 17.В частности, я хочу повторить поведение обновления, то есть шанс назначить / скопировать-создать shared_ptr< Derived > на shared_ptr< Base > без явного приведения.

Проверка типа работает, но я получаю ошибку segfaultдля бесконечного цикла в конструкторе копирования, при попытке скопировать или назначить производный объект.

Общий шаблон-класс

#pragma once

#include <memory>
#include <type_traits>

template <typename T>
class Generic {
    public:
        template <typename DerivedT>
        using Assignable = typename std::enable_if<std::is_assignable<T, DerivedT>::value, Generic<T> &>::type;

        Generic() : _ptr(nullptr) {}


        Generic(T *ptr) : _ptr{ptr} {};

        Generic(Generic && cptr) :
                _ptr(std::move(cptr._ptr))
        {}

        Generic(const Generic & cptr) :
                _ptr{cptr._ptr}
        {}

        template <typename DerivedT, typename = Assignable<DerivedT>>
        Generic(const Generic<DerivedT> &cptr) 
            : Generic(static_cast<const Generic &>(cptr)._ptr)
        {}

        ~Generic() = default;

        Generic & operator=(Generic && cptr) = default;

        Generic & operator=(const Generic & cptr) {
            _ptr = cptr._ptr;
            return *this;
        }

        template <typename DerivedT>
        Assignable<DerivedT> operator=(const Generic<DerivedT> &cptr) {
            _ptr = static_cast<const Generic &>(cptr)._ptr;
            return *this;
        }

    private:
        T* _ptr;
};

main.cpp

#include "Generic.hpp"

struct Base {
};

struct Derived : public Base {
};

int main() {
    Generic<Derived> derived = Generic<Derived>();
    Generic<Base> base(derived);
    //Generic<Base> base = derived;
}

1 Ответ

4 голосов
/ 05 июня 2019

Ваш вопрос может быть уменьшен до:

template <typename T>
class Generic {
    public:
        Generic() = default;

        Generic(T *ptr) : _ptr{ptr} {};

        template <typename Derived>
        Generic(const Generic<Derived> &cptr) :
          Generic(static_cast<const Generic &>(cptr)._ptr)
        {}

    private:
        T* _ptr = nullptr;
};

struct Base {
};

struct Derived : public Base {
};

int main() {
    Generic<Derived> derived = Generic<Derived>();
    Generic<Base> base(derived);
}

Проблема в том, что с static_cast<const Generic &>(cptr) вы неявно создаете экземпляр Generic<T> формы Generic<Derived>, рекурсивно вызывая конструктор до бесконечности .

Возможное исправление:

template <typename T>
class Generic {
    public:
        Generic() = default;

        Generic(T *ptr) : _ptr{ptr} {};

        template <typename Derived>
        Generic(const Generic<Derived> &cptr) :
          Generic(cptr._ptr)
        {}

    private:
        T* _ptr = nullptr;

    template <typename U>
    friend class Generic;
};

struct Base {
};

struct Derived : public Base {
};

int main() {
    Generic<Derived> derived = Generic<Derived>();
    Generic<Base> base(derived);
}
...