Under two circumstances can two distinct instantiations of impl_ptr have a copier function convertible to the other impl_ptr? This copy assignment operator is templated, but I can't see a situation where two distinct impl_ptr instantiations could be used in an assignment.
I have discovered this problem as a result of my testing. I get why a single deleter function can be used for different types (the types could have inheritance relations and virtual destructors), but having compatible copier functions is weird. Even if such case would exist, what is
return impl_ptr(
base_type::ptr_ ? copier_(base_type::ptr_.get()) : nullptr,
base_type::ptr_.get_deleter(),
copier_);
supposed to do in that case?
Let's consider the following example impl_ptr<BaseType, BaseDeleter, BaseCopier>{} = impl_ptr<DerivedType, BaseDeleter, BaseCopier>{}; DerivedType * is convertible to BaseType *. They have virtual destructors, so a shared BaseDeleter can be used.
Type requirements are satisfied for operator=, so it proceeds into the clone(). The clone calls a impl_ptr<DerivedType>(BaseType *, BaseDeleter function, BaseCopier function). Derived type cannot be trivially constructed from a pointer to BaseType (and even if it had a converting constructor, clone() would still fail, because BaseType * cannot be converted to DerivedType * without a cast (which would likely lead to UB)). Also, there is no constructor of impl_ptr accepting a templated pointer, copier function and a deleter function.
Under two circumstances can two distinct instantiations of
impl_ptrhave a copier function convertible to the otherimpl_ptr? This copy assignment operator is templated, but I can't see a situation where two distinctimpl_ptrinstantiations could be used in an assignment.I have discovered this problem as a result of my testing. I get why a single deleter function can be used for different types (the types could have inheritance relations and virtual destructors), but having compatible copier functions is weird. Even if such case would exist, what is
supposed to do in that case?
Let's consider the following example
impl_ptr<BaseType, BaseDeleter, BaseCopier>{} = impl_ptr<DerivedType, BaseDeleter, BaseCopier>{};DerivedType *is convertible toBaseType *. They have virtual destructors, so a sharedBaseDeletercan be used.Type requirements are satisfied for
operator=, so it proceeds into theclone(). The clone calls aimpl_ptr<DerivedType>(BaseType *, BaseDeleter function, BaseCopier function).Derivedtype cannot be trivially constructed from a pointer toBaseType(and even if it had a converting constructor,clone()would still fail, becauseBaseType *cannot be converted toDerivedType *without a cast (which would likely lead to UB)). Also, there is no constructor ofimpl_ptraccepting a templated pointer, copier function and a deleter function.