shared_ptr invalid covariant return type
This pattern/idiom is well-established: struct A { virtual A* clone () = 0; }; struct B : public A { virtual B* clone (); }; But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>: struct A { virtual shared_ptr<A> clone () = 0; }; struct B : public A { virtual shared_ptr<B> clone (); }; Is there a workaround? Another way to solve the same problem? Thanks! -John
John skrev:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>:
struct A { virtual shared_ptr<A> clone () = 0; };
struct B : public A { virtual shared_ptr<B> clone (); };
Is there a workaround? Another way to solve the same problem? Thanks!
Well, don't use shared_ptr here. A newly created clone is not shared, and it makes the interface less usable in other context to return a shared ptr here. -Thorsten
Thorsten Ottosen wrote:
John skrev:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>:
struct A { virtual shared_ptr<A> clone () = 0; };
struct B : public A { virtual shared_ptr<B> clone (); };
Is there a workaround? Another way to solve the same problem? Thanks!
Well, don't use shared_ptr here. A newly created clone is not shared, and it makes the interface less usable in other context to return a shared ptr here.
-Thorsten
Hah! Well, yes, I guess it's true that there's always an option not to use smart pointers at all. :) Perhaps people will make too many assumptions about the method based on the name, "clone". Instead, pretend the clone() method is named something else, without any other assumptions. For example, pretend it's a get_row() method for a database cursor abstraction. The method might trigger a fetch, in which case the returned object is not shared, or it might return a previously fecthed row. Also, whether this method is usable in non-smart-pointer context is not a concern. -John
John wrote:
Thorsten Ottosen wrote:
John skrev:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>:
struct A { virtual shared_ptr<A> clone () = 0; };
struct B : public A { virtual shared_ptr<B> clone (); };
Is there a workaround? Another way to solve the same problem? Thanks!
Well, don't use shared_ptr here. A newly created clone is not shared, and it makes the interface less usable in other context to return a shared ptr here.
-Thorsten
Hah! Well, yes, I guess it's true that there's always an option not to use smart pointers at all. :)
auto_ptr would be more appropriate, and nearly as safe. The other way to solve the same problem is to have the virtual clone always return shared_ptr<A> and add another member function that returns shared_ptr<B>. Have the virtual function call that one. Cheers, -- Dave Abrahams BoostPro Computing http://www.boostpro.com
David Abrahams wrote:
John wrote:
Thorsten Ottosen wrote:
John skrev:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>:
struct A { virtual shared_ptr<A> clone () = 0; };
struct B : public A { virtual shared_ptr<B> clone (); };
Is there a workaround? Another way to solve the same problem? Thanks! Well, don't use shared_ptr here. A newly created clone is not shared, and it makes the interface less usable in other context to return a shared ptr here.
-Thorsten
Hah! Well, yes, I guess it's true that there's always an option not to use smart pointers at all. :)
auto_ptr would be more appropriate, and nearly as safe.
The other way to solve the same problem is to have the virtual clone always return shared_ptr<A> and add another member function that returns shared_ptr<B>. Have the virtual function call that one.
Cheers,
Hi, I don't follow -- why is auto_ptr more appropriate? -John
John wrote:
Thorsten Ottosen wrote:
John skrev:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>:
struct A { virtual shared_ptr<A> clone () = 0; };
struct B : public A { virtual shared_ptr<B> clone (); };
Is there a workaround? Another way to solve the same problem? Thanks!
Well, don't use shared_ptr here. A newly created clone is not shared, and it makes the interface less usable in other context to return a shared ptr here.
-Thorsten
Hah! Well, yes, I guess it's true that there's always an option not to use smart pointers at all. :) Perhaps people will make too many assumptions about the method based on the name, "clone". Instead, pretend the clone() method is named something else, without any other assumptions.
For example, pretend it's a get_row() method for a database cursor abstraction. The method might trigger a fetch, in which case the returned object is not shared, or it might return a previously fecthed row. Also, whether this method is usable in non-smart-pointer context is not a concern. -John
If you are interested in a more generalized approach of the same problem, you should have alook at http://preview.tinyurl.com/3w6elg Greetings from Bremen, Daniel Krügler
I read the discussion and tried it using gcc version 3.2.3 I am getting the following error.
clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns
clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns
clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns
clone2.cpp: In member function `std::auto_ptr<Derived>
Cloneable<Derived>::clone() const [with Derived = A]':
clone2.cpp:50: instantiated from here
clone2.cpp:17: invalid static_cast from type `CloneableBase*' to type `A*
I think my code is right. Any thoughts?
#include <memory>
class CloneableBase {
public:
virtual ~CloneableBase() {};
protected:
virtual CloneableBase* doClone() const = 0 ;
};
template < typename Derived >
class Cloneable : public virtual CloneableBase {
public:
typedef std::auto_ptr<Derived> AutoPtr;
AutoPtr clone() const {
return AutoPtr( static_cast
Thorsten Ottosen wrote:
John skrev:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>:
struct A { virtual shared_ptr<A> clone () = 0; };
struct B : public A { virtual shared_ptr<B> clone (); };
Is there a workaround? Another way to solve the same problem? Thanks!
Well, don't use shared_ptr here. A newly created clone is not shared, and it makes the interface less usable in other context to return a shared ptr here.
-Thorsten
Hah! Well, yes, I guess it's true that there's always an option not to use smart pointers at all. :) Perhaps people will make too many assumptions about the method based on the name, "clone". Instead, pretend the clone() method is named something else, without any other assumptions.
For example, pretend it's a get_row() method for a database cursor abstraction. The method might trigger a fetch, in which case the returned object is not shared, or it might return a previously fecthed row. Also, whether this method is usable in non-smart-pointer context is not a concern. -John
If you are interested in a more generalized approach of the same problem, you should have alook at http://preview.tinyurl.com/3w6elg Greetings from Bremen, Daniel Krügler _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This message is intended only for the personal and confidential use of the designated recipient(s) named above. If you are not the intended recipient of this message you are hereby notified that any review, dissemination, distribution or copying of this message is strictly prohibited. This communication is for information purposes only and should not be regarded as an offer to sell or as a solicitation of an offer to buy any financial product, an official confirmation of any transaction, or as an official statement of Lehman Brothers. Email transmission cannot be guaranteed to be secure or error-free. Therefore, we do not represent that this information is complete or accurate and it should not be relied upon as such. All information is subject to change without notice. -------- IRS Circular 230 Disclosure: Please be advised that any discussion of U.S. tax matters contained within this communication (including any attachments) is not intended or written to be used and cannot be used for the purpose of (i) avoiding U.S. tax related penalties or (ii) promoting, marketing or recommending to another party any transaction or matter addressed herein.
Khandelwal, Amit wrote:
I read the discussion and tried it using gcc version 3.2.3 I am getting the following error.
clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp: In member function `std::auto_ptr<Derived> Cloneable<Derived>::clone() const [with Derived = A]': clone2.cpp:50: instantiated from here
clone2.cpp:17: invalid static_cast from type `CloneableBase*' to type `A*
I think my code is right. Any thoughts?
#include <memory>
class CloneableBase { public: virtual ~CloneableBase() {};
protected: virtual CloneableBase* doClone() const = 0 ; };
template < typename Derived > class Cloneable : public virtual CloneableBase { public: typedef std::auto_ptr<Derived> AutoPtr; AutoPtr clone() const { return AutoPtr( static_cast
( doClone() ) ); } }; template < typename Derived > class NaturallyCloneable : public virtual CloneableBase { protected:
virtual CloneableBase* doClone() const { return new Derived( static_cast< const Derived& >(*this) ); } };
class A : public Cloneable<A> {}; // abstract base for B and C class B : public A, public NaturallyCloneable<B> {}; class C : public A { virtual A* doClone() const { return new C; } };
int main() { { B b; C c; b.clone(); c.clone(); } }
This problem was discussed in the thread and lead to the final implementation that uses dynamic_cast instead of static_cast inside of Cloneable. The final solution also provides a nice SmartPointer policy, which solves the problem of the return type (Note that the pointer policy does even support naked pointers as well). Greetings from Bremen, Daniel
I am reading through the paper by B. Strousstrup on multiple inheritance and this is what is says. A class can have an arbitrary number of virtual base classes. One can cast from a derived class to a virtual base class, but not from a virtual base class to a derived class. The former involves following the virtual base pointer; the latter cannot be done given the information available at run time. Storing a ''back-pointer'' to the enclosing object(s) is non-trivial in general and was considered unsuitable for C++ as was the alternative strategy of dynamically keeping track of the objects ''for which'' a given member function invocation operates. The uses of such a back-pointer mechanism did not seem to warrant the added implementation complexity, the extra space required in objects, and the added run-time cost of initialization. class Cloneable : public virtual CloneableBase { .. } As per B. Strousstrup we cannot do it. Am I missing something obvious? I have also seen the following error - something expected .. cannot convert from base `CloneableBase' to derived type `A' via virtual base `CloneableBase I will read through the entire chain again. Appreciate your time. -----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Daniel Krügler Sent: Wednesday, June 25, 2008 9:45 AM To: boost-users@lists.boost.org Subject: Re: [Boost-users] shared_ptr invalid covariant return type Khandelwal, Amit wrote:
I read the discussion and tried it using gcc version 3.2.3 I am getting the following error.
clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:34: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp: In member function `std::auto_ptr<Derived> Cloneable<Derived>::clone() const [with Derived = A]': clone2.cpp:50: instantiated from here
clone2.cpp:17: invalid static_cast from type `CloneableBase*' to type `A*
I think my code is right. Any thoughts?
#include <memory>
class CloneableBase { public: virtual ~CloneableBase() {};
protected: virtual CloneableBase* doClone() const = 0 ; };
template < typename Derived > class Cloneable : public virtual CloneableBase { public: typedef std::auto_ptr<Derived> AutoPtr; AutoPtr clone() const { return AutoPtr( static_cast
( doClone() ) ); } }; template < typename Derived > class NaturallyCloneable : public virtual CloneableBase { protected:
virtual CloneableBase* doClone() const { return new Derived( static_cast< const Derived& >(*this) ); } };
class A : public Cloneable<A> {}; // abstract base for B and C class B : public A, public NaturallyCloneable<B> {}; class C : public A { virtual A* doClone() const { return new C; } };
int main() { { B b; C c; b.clone(); c.clone(); } }
This problem was discussed in the thread and lead to the final implementation that uses dynamic_cast instead of static_cast inside of Cloneable. The final solution also provides a nice SmartPointer policy, which solves the problem of the return type (Note that the pointer policy does even support naked pointers as well). Greetings from Bremen, Daniel _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This message is intended only for the personal and confidential use of the designated recipient(s) named above. If you are not the intended recipient of this message you are hereby notified that any review, dissemination, distribution or copying of this message is strictly prohibited. This communication is for information purposes only and should not be regarded as an offer to sell or as a solicitation of an offer to buy any financial product, an official confirmation of any transaction, or as an official statement of Lehman Brothers. Email transmission cannot be guaranteed to be secure or error-free. Therefore, we do not represent that this information is complete or accurate and it should not be relied upon as such. All information is subject to change without notice. -------- IRS Circular 230 Disclosure: Please be advised that any discussion of U.S. tax matters contained within this communication (including any attachments) is not intended or written to be used and cannot be used for the purpose of (i) avoiding U.S. tax related penalties or (ii) promoting, marketing or recommending to another party any transaction or matter addressed herein.
Khandelwal, Amit wrote:
I am reading through the paper by B. Strousstrup on multiple inheritance and this is what is says.
A class can have an arbitrary number of virtual base classes. One can cast from a derived class to a virtual base class, but not from a virtual base class to a derived class. The former involves following the virtual base pointer; the latter cannot be done given the information available at run time.
[Sorry for my late answer, yesterday I was out-of-office basically the complete day.] Correct, it cannot be done for the general case due to potential ambiguity ("path-split"). But in can be done, if the dynamic_cast mechanism follows a unique path.
Storing a ''back-pointer'' to the enclosing object(s) is non-trivial in general and was considered unsuitable for C++ as was the alternative strategy of dynamically keeping track of the objects ''for which'' a given member function invocation operates. The uses of such a back-pointer mechanism did not seem to warrant the added implementation complexity, the extra space required in objects, and the added run-time cost of initialization.
class Cloneable : public virtual CloneableBase { .. }
As per B. Strousstrup we cannot do it. Am I missing something obvious?
It be done, but there are exceptions, where it cannot be done.
For example there is no problem in the quoted cases:
class A : public Cloneable<A> {}; // abstract base for B and C
class B : public A, public NaturallyCloneable<B> {};
class C : public A {
virtual A* doClone() const {
return new C;
}
};
The reason is because CloneableBase is an abstract class, so during the
doClone() call of the clone function in Cloneable you will invoke
doClone() of an even more derived class as Cloneable itself is.
The result of doClone() will be a CloneableBase* to this derived
object. The dynamic_cast attempty now to find this derived class and
in many cases this search will be successful, see e.g. this extended
example from [expr.dynamic.cast]/9:
class A { virtual void f() {} };
class B { virtual void g() {} };
class D : public virtual A, private B { };
class E : public D, public B { };
class F : public E, public D { };
int main() {
F f;
A* ap = &f; // succeeds: finds unique A
D* dp = dynamic_cast
I have also seen the following error - something expected ..
cannot convert from base `CloneableBase' to derived type `A' via virtual base `CloneableBase
Please provide a concrete example, currently I do not see where this should apply for the finally provided solution via dynamic_cast. Greetings from Bremen, Daniel Krügler
It all seems to make sense now. In the paper only diamond inheritance was discussed. When I was playing around with the code I may have constructed a diamond inheritance which led to that problem. Your explanation has cleared things up for me. Appreciate your response.
I have one final question.
If I define class C as below everything compiles correctly.
class C : public A {
virtual CloneableBase * doClone() const {
}
};
However, if I define it as described in the post I get the following error.
clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns
clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns
clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns
class C : public A {
virtual A* doClone() const {
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
#include <memory>
class CloneableBase {
public:
virtual ~CloneableBase() {};
protected:
virtual CloneableBase* doClone() const = 0 ;
};
template < typename Derived >
class Cloneable : public virtual CloneableBase {
public:
typedef std::auto_ptr<Derived> AutoPtr;
AutoPtr clone() const {
return AutoPtr( dynamic_cast
I am reading through the paper by B. Strousstrup on multiple inheritance and this is what is says.
A class can have an arbitrary number of virtual base classes. One can cast from a derived class to a virtual base class, but not from a virtual base class to a derived class. The former involves following the virtual base pointer; the latter cannot be done given the information available at run time.
[Sorry for my late answer, yesterday I was out-of-office basically the complete day.] Correct, it cannot be done for the general case due to potential ambiguity ("path-split"). But in can be done, if the dynamic_cast mechanism follows a unique path.
Storing a ''back-pointer'' to the enclosing object(s) is non-trivial in general and was considered unsuitable for C++ as was the alternative strategy of dynamically keeping track of the objects ''for which'' a given member function invocation operates. The uses of such a back-pointer mechanism did not seem to warrant the added implementation complexity, the extra space required in objects, and the added run-time cost of initialization.
class Cloneable : public virtual CloneableBase { .. }
As per B. Strousstrup we cannot do it. Am I missing something obvious?
It be done, but there are exceptions, where it cannot be done.
For example there is no problem in the quoted cases:
class A : public Cloneable<A> {}; // abstract base for B and C class B : public A, public NaturallyCloneable<B> {}; class C : public A {
virtual A* doClone() const {
return new C;
}
};
The reason is because CloneableBase is an abstract class, so during the
doClone() call of the clone function in Cloneable you will invoke
doClone() of an even more derived class as Cloneable itself is.
The result of doClone() will be a CloneableBase* to this derived object. The dynamic_cast attempty now to find this derived class and in many cases this search will be successful, see e.g. this extended example from [expr.dynamic.cast]/9:
class A { virtual void f() {} };
class B { virtual void g() {} };
class D : public virtual A, private B { }; class E : public D, public B { }; class F : public E, public D { };
int main() {
F f;
A* ap = &f; // succeeds: finds unique A
D* dp = dynamic_cast
I have also seen the following error - something expected ..
cannot convert from base `CloneableBase' to derived type `A' via virtual base `CloneableBase
Please provide a concrete example, currently I do not see where this should apply for the finally provided solution via dynamic_cast. Greetings from Bremen, Daniel Krügler _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This message is intended only for the personal and confidential use of the designated recipient(s) named above. If you are not the intended recipient of this message you are hereby notified that any review, dissemination, distribution or copying of this message is strictly prohibited. This communication is for information purposes only and should not be regarded as an offer to sell or as a solicitation of an offer to buy any financial product, an official confirmation of any transaction, or as an official statement of Lehman Brothers. Email transmission cannot be guaranteed to be secure or error-free. Therefore, we do not represent that this information is complete or accurate and it should not be relied upon as such. All information is subject to change without notice. -------- IRS Circular 230 Disclosure: Please be advised that any discussion of U.S. tax matters contained within this communication (including any attachments) is not intended or written to be used and cannot be used for the purpose of (i) avoiding U.S. tax related penalties or (ii) promoting, marketing or recommending to another party any transaction or matter addressed herein.
AMDG Khandelwal, Amit wrote:
However, if I define it as described in the post I get the following error.
clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns
class C : public A { virtual A* doClone() const { } };
You're using a pretty old compiler, you know. In Christ, Steven Watanabe
Khandelwal, Amit wrote:
It all seems to make sense now. In the paper only diamond inheritance was discussed. When I was playing around with the code I may have constructed a diamond inheritance which led to that problem. Your explanation has cleared things up for me. Appreciate your response.
I have one final question.
If I define class C as below everything compiles correctly.
class C : public A { virtual CloneableBase * doClone() const { } };
However, if I define it as described in the post I get the following error.
clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns
class C : public A { virtual A* doClone() const { } };
Which compiler (including version) are you using? It looks an error to me, if it rejects the code below. If it was gcc 3.2.3, try a newer one (like 4.1.2), and you will see that the code compiles smoothly. - Daniel
//////////////////////////////////////////////////////////////////////////////////////////////// #include <memory>
class CloneableBase { public: virtual ~CloneableBase() {};
protected: virtual CloneableBase* doClone() const = 0 ; };
template < typename Derived > class Cloneable : public virtual CloneableBase { public: typedef std::auto_ptr<Derived> AutoPtr; AutoPtr clone() const { return AutoPtr( dynamic_cast
( doClone()) ); } }; template < typename Derived > class NaturallyCloneable : public virtual CloneableBase { protected:
virtual CloneableBase* doClone() const { return new Derived( dynamic_cast< const Derived& >(*this) ); } };
class A : public Cloneable<A> {}; class B : public A, public NaturallyCloneable<B> {};
class C : public A { virtual A* doClone() const { return new C; } };
int main() { }
-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Daniel Krügler Sent: Friday, June 27, 2008 5:21 AM To: boost-users@lists.boost.org Subject: Re: [Boost-users] shared_ptr invalid covariant return type
Khandelwal, Amit wrote:
I am reading through the paper by B. Strousstrup on multiple inheritance and this is what is says.
A class can have an arbitrary number of virtual base classes. One can cast from a derived class to a virtual base class, but not from a virtual base class to a derived class. The former involves following the virtual base pointer; the latter cannot be done given the information available at run time.
[Sorry for my late answer, yesterday I was out-of-office basically the complete day.]
Correct, it cannot be done for the general case due to potential ambiguity ("path-split"). But in can be done, if the dynamic_cast mechanism follows a unique path.
Storing a ''back-pointer'' to the enclosing object(s) is non-trivial in general and was considered unsuitable for C++ as was the alternative strategy of dynamically keeping track of the objects ''for which'' a given member function invocation operates. The uses of such a back-pointer mechanism did not seem to warrant the added implementation complexity, the extra space required in objects, and the added run-time cost of initialization.
class Cloneable : public virtual CloneableBase { .. }
As per B. Strousstrup we cannot do it. Am I missing something obvious?
It be done, but there are exceptions, where it cannot be done. For example there is no problem in the quoted cases:
class A : public Cloneable<A> {}; // abstract base for B and C class B : public A, public NaturallyCloneable<B> {}; class C : public A { virtual A* doClone() const { return new C; } };
The reason is because CloneableBase is an abstract class, so during the doClone() call of the clone function in Cloneable you will invoke doClone() of an even more derived class as Cloneable itself is. The result of doClone() will be a CloneableBase* to this derived object. The dynamic_cast attempty now to find this derived class and in many cases this search will be successful, see e.g. this extended example from [expr.dynamic.cast]/9:
class A { virtual void f() {} }; class B { virtual void g() {} }; class D : public virtual A, private B { }; class E : public D, public B { }; class F : public E, public D { };
int main() { F f; A* ap = &f; // succeeds: finds unique A D* dp = dynamic_cast
(ap); //#1 fails: yields 0 // f has two D subobjects F* fp = dynamic_cast (ap); //#2 E e; ap = &e; E* ep = dynamic_cast (ap); //#3 } #1 fails, because via ap you can find two different D subobjects.
#2 and #3 both succeed, because there is only *one* object F that can be found and similar for the E object.
The rules for these cases are described in [expr.dynamic.cast]/8, which handle the runtime check of dynamic_cast.
I have also seen the following error - something expected ..
cannot convert from base `CloneableBase' to derived type `A' via virtual base `CloneableBase
Please provide a concrete example, currently I do not see where this should apply for the finally provided solution via dynamic_cast.
Greetings from Bremen,
Daniel Krügler
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
This message is intended only for the personal and confidential use of the designated recipient(s) named above. If you are not the intended recipient of this message you are hereby notified that any review, dissemination, distribution or copying of this message is strictly prohibited. This communication is for information purposes only and should not be regarded as an offer to sell or as a solicitation of an offer to buy any financial product, an official confirmation of any transaction, or as an official statement of Lehman Brothers. Email transmission cannot be guaranteed to be secure or error-free. Therefore, we do not represent that this information is complete or accurate and it should not be relied upon as such. All information is subject to change without notice.
-------- IRS Circular 230 Disclosure: Please be advised that any discussion of U.S. tax matters contained within this communication (including any attachments) is not intended or written to be used and cannot be used for the purpose of (i) avoiding U.S. tax related penalties or (ii) promoting, marketing or recommending to another party any transaction or matter addressed herein.
I am using gcc 3.2.3 gcc --version gcc (GCC) 3.2.3 20030502 (Red Hat Linux 3.2.3-56) Copyright (C) 2002 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. I guess it's time to upgrade :) I just wanted to ensure that I am understanding the concept correctly. Thanks everyone for their response. -----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Daniel Krügler Sent: Friday, June 27, 2008 9:48 AM To: boost-users@lists.boost.org Subject: Re: [Boost-users] shared_ptr invalid covariant return type Khandelwal, Amit wrote:
It all seems to make sense now. In the paper only diamond inheritance was discussed. When I was playing around with the code I may have constructed a diamond inheritance which led to that problem. Your explanation has cleared things up for me. Appreciate your response.
I have one final question.
If I define class C as below everything compiles correctly.
class C : public A { virtual CloneableBase * doClone() const { } };
However, if I define it as described in the post I get the following error.
clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns clone2.cpp:33: sorry, not implemented: adjusting pointers for covariant returns
class C : public A { virtual A* doClone() const { } };
Which compiler (including version) are you using? It looks an error to me, if it rejects the code below. If it was gcc 3.2.3, try a newer one (like 4.1.2), and you will see that the code compiles smoothly. - Daniel
////////////////////////////////////////////////////////////////////// ////////////////////////// #include <memory>
class CloneableBase { public: virtual ~CloneableBase() {};
protected: virtual CloneableBase* doClone() const = 0 ; };
template < typename Derived > class Cloneable : public virtual CloneableBase { public: typedef std::auto_ptr<Derived> AutoPtr; AutoPtr clone() const { return AutoPtr( dynamic_cast
( doClone()) ); } }; template < typename Derived > class NaturallyCloneable : public virtual CloneableBase { protected:
virtual CloneableBase* doClone() const { return new Derived( dynamic_cast< const Derived& >(*this) ); } };
class A : public Cloneable<A> {}; class B : public A, public NaturallyCloneable<B> {};
class C : public A { virtual A* doClone() const { return new C; } };
int main() { }
-----Original Message----- From: boost-users-bounces@lists.boost.org [mailto:boost-users-bounces@lists.boost.org] On Behalf Of Daniel Krügler Sent: Friday, June 27, 2008 5:21 AM To: boost-users@lists.boost.org Subject: Re: [Boost-users] shared_ptr invalid covariant return type
Khandelwal, Amit wrote:
I am reading through the paper by B. Strousstrup on multiple inheritance and this is what is says.
A class can have an arbitrary number of virtual base classes. One can cast from a derived class to a virtual base class, but not from a virtual base class to a derived class. The former involves following the virtual base pointer; the latter cannot be done given the information available at run time.
[Sorry for my late answer, yesterday I was out-of-office basically the complete day.]
Correct, it cannot be done for the general case due to potential ambiguity ("path-split"). But in can be done, if the dynamic_cast mechanism follows a unique path.
Storing a ''back-pointer'' to the enclosing object(s) is non-trivial in general and was considered unsuitable for C++ as was the alternative strategy of dynamically keeping track of the objects ''for which'' a given member function invocation operates. The uses of such a back-pointer mechanism did not seem to warrant the added implementation complexity, the extra space required in objects, and the added run-time cost of initialization.
class Cloneable : public virtual CloneableBase { .. }
As per B. Strousstrup we cannot do it. Am I missing something obvious?
It be done, but there are exceptions, where it cannot be done. For example there is no problem in the quoted cases:
class A : public Cloneable<A> {}; // abstract base for B and C class B : public A, public NaturallyCloneable<B> {}; class C : public A { virtual A* doClone() const { return new C; } };
The reason is because CloneableBase is an abstract class, so during the doClone() call of the clone function in Cloneable you will invoke doClone() of an even more derived class as Cloneable itself is. The result of doClone() will be a CloneableBase* to this derived object. The dynamic_cast attempty now to find this derived class and in many cases this search will be successful, see e.g. this extended example from [expr.dynamic.cast]/9:
class A { virtual void f() {} }; class B { virtual void g() {} }; class D : public virtual A, private B { }; class E : public D, public B { }; class F : public E, public D { };
int main() { F f; A* ap = &f; // succeeds: finds unique A D* dp = dynamic_cast
(ap); //#1 fails: yields 0 // f has two D subobjects F* fp = dynamic_cast (ap); //#2 E e; ap = &e; E* ep = dynamic_cast (ap); //#3 } #1 fails, because via ap you can find two different D subobjects.
#2 and #3 both succeed, because there is only *one* object F that can be found and similar for the E object.
The rules for these cases are described in [expr.dynamic.cast]/8, which handle the runtime check of dynamic_cast.
I have also seen the following error - something expected ..
cannot convert from base `CloneableBase' to derived type `A' via virtual base `CloneableBase
Please provide a concrete example, currently I do not see where this should apply for the finally provided solution via dynamic_cast.
Greetings from Bremen,
Daniel Krügler
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
This message is intended only for the personal and confidential use of the designated recipient(s) named above. If you are not the intended recipient of this message you are hereby notified that any review, dissemination, distribution or copying of this message is strictly prohibited. This communication is for information purposes only and should not be regarded as an offer to sell or as a solicitation of an offer to buy any financial product, an official confirmation of any transaction, or as an official statement of Lehman Brothers. Email transmission cannot be guaranteed to be secure or error-free. Therefore, we do not represent that this information is complete or accurate and it should not be relied upon as such. All information is subject to change without notice.
-------- IRS Circular 230 Disclosure: Please be advised that any discussion of U.S. tax matters contained within this communication (including any attachments) is not intended or written to be used and cannot be used for the purpose of (i) avoiding U.S. tax related penalties or (ii) promoting, marketing or recommending to another party any transaction or matter addressed herein.
_______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - This message is intended only for the personal and confidential use of the designated recipient(s) named above. If you are not the intended recipient of this message you are hereby notified that any review, dissemination, distribution or copying of this message is strictly prohibited. This communication is for information purposes only and should not be regarded as an offer to sell or as a solicitation of an offer to buy any financial product, an official confirmation of any transaction, or as an official statement of Lehman Brothers. Email transmission cannot be guaranteed to be secure or error-free. Therefore, we do not represent that this information is complete or accurate and it should not be relied upon as such. All information is subject to change without notice. -------- IRS Circular 230 Disclosure: Please be advised that any discussion of U.S. tax matters contained within this communication (including any attachments) is not intended or written to be used and cannot be used for the purpose of (i) avoiding U.S. tax related penalties or (ii) promoting, marketing or recommending to another party any transaction or matter addressed herein.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Tuesday 24 June 2008 09:43 am, John wrote:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>:
struct A { virtual shared_ptr<A> clone () = 0; };
struct B : public A { virtual shared_ptr<B> clone (); };
Is there a workaround? Another way to solve the same problem? Thanks!
See: http://lists.boost.org/boost-users/2003/02/2996.php -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFIYQbo5vihyNWuA4URAgX3AJ4sXKFJod/S/HhSgw9pPl+ODY6qLwCbBk9s navD7l4mPLsoDQpEezfm1a0= =CeuK -----END PGP SIGNATURE-----
Thanks, I did see that in the archives before I posted. Please correct me if I'm wrong, but the code from 2003 doesn't provide a solution, since the clone() method is not virtual. i.e., you can't do this with it: shared_ptr<A> a = foo(); shared_ptr<A> a2 = a->clone (); which is the goal of the pattern. Frank Mori Hess wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Tuesday 24 June 2008 09:43 am, John wrote:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
But the above does not work when the pointers A* and B* are replaced by shared_ptr<A> and shared_ptr<B>:
struct A { virtual shared_ptr<A> clone () = 0; };
struct B : public A { virtual shared_ptr<B> clone (); };
Is there a workaround? Another way to solve the same problem? Thanks!
See:
http://lists.boost.org/boost-users/2003/02/2996.php -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFIYQbo5vihyNWuA4URAgX3AJ4sXKFJod/S/HhSgw9pPl+ODY6qLwCbBk9s navD7l4mPLsoDQpEezfm1a0= =CeuK -----END PGP SIGNATURE----- _______________________________________________ Boost-users mailing list Boost-users@lists.boost.org http://lists.boost.org/mailman/listinfo.cgi/boost-users
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Tuesday 24 June 2008 11:13 am, John wrote:
Thanks, I did see that in the archives before I posted. Please correct me if I'm wrong, but the code from 2003 doesn't provide a solution, since the clone() method is not virtual. i.e., you can't do this with it:
shared_ptr<A> a = foo(); shared_ptr<A> a2 = a->clone ();
which is the goal of the pattern.
The free function solution mentioned in the post calls the virtual do_clone. The virtual do_clone always returns a shared_ptr to the base class, and the template free function downcasts it. The free function has to be a friend, (or do_clone public). -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iD8DBQFIYRpY5vihyNWuA4URAprZAKCUckkFLIb8X50siGh8XR1qW5OuYwCfTj1P wpGOYLt+zYm9oFE+IPT3OR8= =dBMq -----END PGP SIGNATURE-----
John wrote:
This pattern/idiom is well-established:
struct A { virtual A* clone () = 0; };
struct B : public A { virtual B* clone (); };
Except you've quoted it wrong. The return type of virtual functions must always be the same for all classes in the polymorphic hierarchy. This is standard C++ polymorphism - always use a pointer to the superclass to point to all subclasses. The return type in this case should be A* for both cases: struct A { virtual A* clone () = 0; }; struct B : public A { virtual A* clone (); }; --------------^ Similarly, for the shared_ptr version, do the same, i.e. always use shared_ptr<A> to point to objects of types A and B. See the manual for my own smart pointer classes for more on this topic: http://stlplus.sourceforge.net/stlplus3/docs/smart_ptr.html#cloning
Andy Rushton escreveu:
Except you've quoted it wrong. The return type of virtual functions must always be the same for all classes in the polymorphic hierarchy. This is standard C++ polymorphism - always use a pointer to the superclass to point to all subclasses. The return type in this case should be A* for both cases:
And apparently you've forgotten what covariant return types are :) Regards, rod
No, as I read it, the original author was not interested in covariant return types, he was asking how to solve the problem of writing clone methods for polymorphic classes. The solution is to *not* use covariant return types. This avoids the problem where shared_ptr<A> is a completely different and non-castable type to shared_ptr<B> - but if you do it the non-covariant way you don't have this problem because all your clone functions return shared_ptr<A>. Problem solved. You'll get further in life if you make positive contributions, rather than putting down other people's. Rodolfo Lima wrote:
And apparently you've forgotten what covariant return types are :)
Regards, rod
participants (9)
-
Andy Rushton
-
Daniel Krügler
-
David Abrahams
-
Frank Mori Hess
-
John
-
Khandelwal, Amit
-
Rodolfo Lima
-
Steven Watanabe
-
Thorsten Ottosen