Я следую этому руководству, чтобы понять, как возвращать интеллектуальные указатели и ковариацию в C++.
#include <memory>
#include <iostream>
class cloneable
{
public:
virtual ~cloneable() {}
std::unique_ptr<cloneable> clone() const
{
return std::unique_ptr<cloneable>(this->clone_impl());
}
private:
virtual cloneable * clone_impl() const = 0;
};
///////////////////////////////////////////////////////////////////////////////
template <typename Derived, typename Base>
class clone_inherit: public Base
{
public:
std::unique_ptr<Derived> clone() const
{
return std::unique_ptr<Derived>(static_cast<Derived *>(this->clone_impl()));
}
private:
virtual clone_inherit * clone_impl() const override
{
return new Derived(*this); // getting error here
}
};
class concrete : public clone_inherit<concrete, cloneable>
{
};
int main()
{
std::unique_ptr<concrete> c = std::make_unique<concrete>();
}
Когда я выполняю этот пример, я получаю следующую ошибку:
/tmp/0RmVdQYjfA.cpp: In instantiation of 'clone_inherit<Derived, Base>* clone_inherit<Derived, Base>::clone_impl() const [with Derived = concrete; Base = cloneable]':
/tmp/0RmVdQYjfA.cpp:30:28: required from here
/tmp/0RmVdQYjfA.cpp:32:14: error: no matching function for call to 'concrete::concrete(const clone_inherit<concrete, cloneable>&)'
32 | return new Derived(*this);
| ^~~~~~~~~~~~~~~~~~
/tmp/0RmVdQYjfA.cpp:37:7: note: candidate: 'constexpr concrete::concrete()'
37 | class concrete : public clone_inherit<concrete, cloneable>
| ^~~~~~~~
/tmp/0RmVdQYjfA.cpp:37:7: note: candidate expects 0 arguments, 1 provided
/tmp/0RmVdQYjfA.cpp:37:7: note: candidate: 'constexpr concrete::concrete(const concrete&)'
/tmp/0RmVdQYjfA.cpp:37:7: note: no known conversion for argument 1 from 'const clone_inherit<concrete, cloneable>' to 'const concrete&'
/tmp/0RmVdQYjfA.cpp:37:7: note: candidate: 'constexpr concrete::concrete(concrete&&)'
/tmp/0RmVdQYjfA.cpp:37:7: note: no known conversion for argument 1 from 'const clone_inherit<concrete, cloneable>' to 'concrete&&'
Чтобы исправить эту ошибку в строке 32, мне пришлось вернуть именно указатель возврата на производный класс следующим образом:
// return new static_cast<Derived*>(*this); to replace with
return new Derived(static_cast<const Derived&>(*this));
Может ли кто-нибудь предложить правильный способ исправить это?
Вы застряли на C++17? это намного проще с явными аргументами объекта С++ 23
@Калет, можешь сказать это как ответ :) спасибо
Вы можете добавить частный неявный или явный оператор приведения:
template <typename Derived, typename Base>
class clone_inherit : public Base {
public:
std::unique_ptr<Derived> clone() const {
return std::unique_ptr<Derived>(static_cast<Derived *>(this->clone_impl()));
}
private:
// added here
explicit operator const Derived &() const {
return static_cast<const Derived &>(*this);
}
virtual clone_inherit *clone_impl() const override {
return new Derived(*this); // not getting error here
}
};
Привет @3CxEZiVlQ, есть ли другие способы сделать это или это правильный способ сделать это?
В любом случае спасибо
Это спорно, как и весь код в вопросе, но не так уж и плохо.
Благодаря явным аргументам объекта C++ 23 этот шаблон становится очень простым:
class cloneable
{
public:
template<typename Self>
std::unique_ptr<Self> clone(this const Self& self) const {
return std::make_unique<Self>(self);
}
};
Это позволяет избежать необходимости указывать производный тип по мере его вывода.
спасибо, просто чтобы знать)
@wohlstad спасибо, я исправил