尝试"复制"shared_ptr向上转换行为会导致复制构造函数上的无限递归(导致段错误)

Trying to 'replicate' shared_ptr upcasting behaviour cause an infinite recursion on copy constructor (that lead to a segfault)

本文关键字:复制 无限 段错误 错误 构造函数 递归 ptr shared 转换 换行 尝试      更新时间:2023-10-16

我正在尝试复制shared_ptr行为,以更好地理解模板元编程和 c++17 中的type_traits。具体来说,我想复制向上转换行为,即在没有任何显式强制转换的情况下将shared_ptr< Base > shared_ptr< Derived >分配/复制创建的机会。

类型检查正在工作,但是在尝试复制或分配派生对象时,我在复制构造函数中出现无限循环的段错误。

通用模板类

#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;
};

主.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;
}

您的问题可以简化为:

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);
}