Estou escrevendo uma classe herdada de std::unique_ptr
classes que possuem uma função de clone:
template <typename T>
class cl_ptr : public std::unique_ptr<T>
{
public:
cl_ptr() noexcept = default;
cl_ptr(T* p) noexcept : std::unique_ptr<T>(p) {}
cl_ptr(cl_ptr<T> const& cp) : std::unique_ptr<T>(cp ? cp->clone() : nullptr) {}
cl_ptr(cl_ptr<T>&&) noexcept = default;
cl_ptr<T>& operator=(cl_ptr<T> const& cp) { this->reset(cp ? cp->clone() : nullptr); return *this; }
cl_ptr<T>& operator=(cl_ptr<T>&& cp) noexcept = default;
~cl_ptr() noexcept = default;
};
Recebo um erro ao tentar converter de uma instância com type T
para uma instância com type T const
:
cl_ptr<Foo> p1(new Foo);
cl_ptr<Foo const> p2 = p1; // <- Compiler error here
// error: conversion from ‘cl_ptr<Foo>’ to non-scalar type ‘cl_ptr<const Foo>’ requested
Mas não sei como implementá-lo .
Claro que não quero que este código seja compilado:
cl_ptr<Foo const> p1(new Foo);
cl_ptr<Foo> p2 = p1; // <- Always wrong
Exemplo mínimo reproduzível:
# include <memory>
template <typename T>
class cl_ptr : public std::unique_ptr<T>
{
public:
cl_ptr() noexcept = default;
cl_ptr(T* p) noexcept : std::unique_ptr<T>(p) {}
cl_ptr(cl_ptr<T> const& cp) : std::unique_ptr<T>(cp ? cp->clone() : nullptr) {}
cl_ptr(cl_ptr<T>&&) noexcept = default;
cl_ptr<T>& operator=(cl_ptr<T> const& cp) { this->reset(cp ? cp->clone() : nullptr); return *this; }
cl_ptr<T>& operator=(cl_ptr<T>&& cp) noexcept = default;
~cl_ptr() noexcept = default;
};
class Foo
{
public:
Foo() = default;
Foo(Foo const&) = default;
~Foo() noexcept = default;
Foo* clone() const { return new Foo(*this); }
};
int main()
{
cl_ptr<Foo> p1(new Foo);
cl_ptr<Foo const> p2 = p1;
cl_ptr<Foo> p3 = p2; // must fail
return 0;
}
Em geral: você não pode.
A<B const>
eA<B>
poderiam ser coisas completamente diferentes devido à especialização do modelo. Portanto, em geral, as instâncias de modelo de classe com diferentes listas de argumentos de modelo são tratadas como tipos distintos e não relacionados.No caso de ponteiros inteligentes, a biblioteca padrão faz um grande esforço para permitir tais conversões sob certas circunstâncias, mas todas essas são implementações personalizadas para o respectivo modelo de classe.
Você poderia fazer o mesmo para sua classe específica. Mas
cl_ptr
parece um invólucro trivial, entãostd::unique_ptr
por que não usarstd::unique_ptr
diretamente?Vou permitir qualquer conversão de
cl_ptr<T>
paracl_ptr<U>
onde houver uma conversão deT *
paraU *
, assim comostd::unique_ptr
. Isso permiteT *
econst T *
não permiteconst T *
,T *
mas tambémDerived *
incluiBase *
etc.