On 5 Mai, 15:10, Krzysiek Czain'ski "Czajnik" <1czaj...@[EMAIL PROTECTED]
>
wrote:
> I'm working on a Cloneable concept for C++ cl*****. Here's what I
> think it should be like, however it doesn't compile :?
>
> #include <memory>
>
> class CloneableBase
> {
> public:
> virtual ~CloneableBase() {}
> private:
> virtual CloneableBase* doClone() const = 0;
>
> };
>
> template < typename Derived >
> class Cloneable : public CloneableBase
> {
> public:
> typedef std::auto_ptr<Derived> AutoPtr;
> AutoPtr clone() const { return AutoPtr( doClone() ); }
> private:
> virtual Derived* doClone() const = 0; // COMPILE ERROR
>
> };
>
> class A : public Cloneable<A>
> {
> virtual A* doClone() { return new A( *this ); }
Probably you meant a const version of this:
virtual A* doClone() const { return new A( *this ); }
>
> };
>
> int main()
> {
> {
> A a;
> A::AutoPtr pa = a.clone();
> }
>
> }
>
> cloneable.cpp:17: error: invalid covariant return type for 'Derived*
> Cloneable<Derived>::doClone() const [with Derived = A]'
> gcc version 4.2.3 (Gentoo 4.2.3 p1.0)
>
> MSVC.NET 2005 gives a similar error on this..
>
> The C++ standard says (10.3.5)
> The return type of an overriding function shall be either identical to
> the return type of the
> overridden function or covariant with the cl***** of the functions.
>
> Since Derived = A, it is derived from CloneableBase, through
> Cloneable<A>, so Derived is covariant with CloneableBase.
>
> Is my understanding wrong, or is this the compilers' bug, or maybe
> it's just the way it is supposed to be, and I misunderstand the C++
> standard? I'm also looking forward to comments on design/style.
First, the compilers are all correct to reject your
code, as others have already mentioned. The reason
is that you omitted to mention this part from p. 5:
"If the return type of D::f differs from the return type of
B::f, the class type in the return type of D::f shall be
complete at the point of declaration of D::f or shall be the
class type D."
In your example, Derived is incomplete at this point and
different from Cloneable.
Your problem is an interesting one and has been
issued several times (longer than a decade) in this
an related newsgroups.
IMO the next matching solution given your ansatz
is the following one. It deviates only slightly
from your original approach in that it makes the
CloneableBase::doClone() protected instead of
private - I don't think that this causes much
harm. The class template uses a static_cast, but
this should be safe, because it still is an
abstract class (implicitly) and if the provided
template parameter does not correspond to a
derived class, it should not compile.[1]
#include <memory>
class CloneableBase
{
public:
virtual ~CloneableBase() {}
protected:
virtual CloneableBase* doClone() const = 0;
};
template < typename Derived>
class Cloneable : public CloneableBase
{
public:
typedef std::auto_ptr<Derived> AutoPtr;
AutoPtr clone() const {
return AutoPtr( static_cast<Derived*>(this->doClone()) );
}
};
class A : public Cloneable<A>
{
virtual A* doClone() const { return new A( *this ); }
};
int main()
{
{
A a;
A::AutoPtr pa = a.clone();
}
}
HTH & Greetings from Bremen,
Daniel Krügler
[1] If you really want, you can outsmart the mechanism like
this, but I'm not talking about Machiavelli:
class B : public Cloneable<A> {
virtual A* doClone() const { return new A(); }
};
--
[ See http://www.gotw.ca/resources/clcm.htm
for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


|