定义类模板构造函数的两种方法之间的区别

Difference between two ways of defining class template constructors

本文关键字:两种 方法 之间 区别 构造函数 定义      更新时间:2023-10-16

我正在尝试实现我的自定义版本的shared_ptr和weak_ptr。在实现它们时,我遇到了一些问题。

为了接受来自构造函数的子类类型,我不得不使用另一个模板类型 U 作为参数,而不是类 'T' 的模板类型。

假设参数类型为 U 的构造函数为版本 1 和参数类型 T 作为版本 2 的构造函数

这是我的问题

  • 为什么当我使用相同类型初始化类模板时会调用版本 2?
WeakPtr<T> weakPtr_a;
WeakPtr<T> weakPtr_b = weakPtr_a; //Shouldn't this be still enough with version 1?
  • 在哪些情况下会调用版本 2?这种实现可行吗?

这是我的代码片段

WeakPtr-Decl.hpp

template <typename T>
class WeakPtr
{
T* m_objectPtr = nullptr;
SharedObjectInfo* m_sharedObjectInfoPtr = nullptr;
template <typename U>
friend class WeakPtr;
public:
constexpr WeakPtr();
~WeakPtr() = default;
//! Copy constructor
//! tparam U : template type of weakPtr to copy from
//! U must be same type as T or subclass of T or assertion will fail
//! param weakPtr : weakPtr to copy from
template <typename U>
WeakPtr(const WeakPtr<U>& weakPtr); // Version 1
//! Copy constructor
//! param weakPtr : weakPtr to copy from
WeakPtr(const WeakPtr<T>& weakPtr); // Version 2
};

WeakPtr-Impl.hpp

template <typename T>
template <typename U>
WeakPtr<T>::WeakPtr(const WeakPtr<U>& weakPtr)
: m_objectPtr(weakPtr.m_objectPtr),
m_sharedObjectInfoPtr(weakPtr.m_sharedObjectInfoPtr)
{
static_assert(std::is_same<std::decay_t<T>, std::decay_t<U>>::value ||
std::is_base_of<std::decay_t<T>, std::decay_t<U>>::value);
}
template <typename T>
WeakPtr<T>::WeakPtr(const WeakPtr<T>& weakPtr)
: m_objectPtr(weakPtr.m_objectPtr),
m_sharedObjectInfoPtr(weakPtr.m_sharedObjectInfoPtr)
{
}

如注释中所述,当可用的确切非模板构造函数(版本 2,在您的情况下(时,它优先于模板构造函数(版本 1(。

我建议使用委托构造函数,所以让编译器选择版本 2(当类型相同时(,并从版本 2 调用版本 1。

我建议在版本 1 中添加一个未使用的默认第二个参数

template <typename U> // ............VVVVVVV
WeakPtr (WeakPtr<U> const & weakPtr, int = 0)
: m_objectPtr{weakPtr.m_objectPtr},
m_sharedObjectInfoPtr{weakPtr.m_sharedObjectInfoPtr}
{  }

并按如下方式转换版本 2

WeakPtr (WeakPtr const & weakPtr) : WeakPtr{weakPtr, 0}
{ }

添加了版本 1中第二个未使用的参数,以允许选择从版本 2 调用的版本 1。

观察版本 2中委派调用时WeakPtr{weakPtr, 0}中的零:添加的零强制调用版本 1,因为版本 2 只有一个参数。

默认值对于允许在类型不同时直接使用版本 1 非常重要。