Virtual constructor & covariant return types

A

Alex Vinokur

Here is a code from
http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8

--------------------------------------
class Shape {
public:
virtual ~Shape() { } // A virtual destructor
virtual void draw() = 0; // A pure virtual function
virtual void move() = 0;
...
virtual Shape* clone() const = 0; // Uses the copy constructor
virtual Shape* create() const = 0; // Uses the default constructor
};

class Circle : public Shape {
public:
Circle* clone() const; // Covariant Return Types; see below
Circle* create() const; // Covariant Return Types; see below
...
};

Circle* Circle::clone() const { return new Circle(*this); }
Circle* Circle::create() const { return new Circle(); }

void userCode(Shape& s)
{
Shape* s2 = s.clone();
Shape* s3 = s.create();
...
delete s2; // You need a virtual destructor here
delete s3;
}

--------------------------------------




It seems that behavior of the program will be the same one if we don't
use covariant return types, for instance

class Circle : public Shape {
public:
Shape* clone() const; // Non-Covariant Return Types
Shape* create() const; // Non-Covariant Return Types
...
};

Why do we need covariant return types in the "virtual constructor"
design pattern?
 
A

Alf P. Steinbach

* Alex Vinokur:
Why do we need covariant return types in the "virtual constructor"
design pattern?

You don't absolutely /need/ them: they're a convenience feature to avoid
casts.

First, where might you actually use a covariant result? When you
statically know the type (or at least, a more specialized type than
Base) of the object you're cloning or using as an examplar. In that
case, you're avoiding a downcast in the client code.

That downcast in the client code can be avoided anyway, but at the cost
of a centralized downcast in the class' code, or the cost of not
supporting derived classes. The covariance feature removes that cost,
but only for built-in pointers and references. For smart-pointers you
have to cope with it and choose your poison.
 
A

Alex Vinokur

Alf P. Steinbach said:
* Alex Vinokur:

You don't absolutely /need/ them: they're a convenience feature to avoid
casts.
[snip]

--- Covariant Return Types ---
class Circle : public Shape {
public:
Circle* clone() const;
Circle* create() const;
...
};

--- Non-Covariant Return Types ---
// Use of this class requires no casts.
class Circle : public Shape {
public:
Shape* clone() const;
Shape* create() const;
...
};
 
R

red floyd

Alex said:
Alf P. Steinbach said:
* Alex Vinokur:
You don't absolutely /need/ them: they're a convenience feature to avoid
casts.
[snip]

--- Covariant Return Types ---
class Circle : public Shape {
public:
Circle* clone() const;
Circle* create() const;
...
};

--- Non-Covariant Return Types ---
// Use of this class requires no casts.
class Circle : public Shape {
public:
Shape* clone() const;
Shape* create() const;
...
};

Your second class does in fact require casts.

Circle aCircle;
Circle* badClone = aCircle.clone(); // ERROR!!!!
Circle* goodClone = static_cast<Circle*>(aCircle.clone);

Note that dynamic_cast would also be acceptable (assuming a virtual
function somewhere).

The first class requires no such casts, and since upcasts to the base
class (Shape) do not require a cast either, that method is preferable.
 
A

Alex Vinokur

red floyd said:
Alex said:
Alf P. Steinbach said:
* Alex Vinokur:
Why do we need covariant return types in the "virtual constructor"
design pattern?
You don't absolutely /need/ them: they're a convenience feature to avoid
casts.
[snip]

--- Covariant Return Types ---
class Circle : public Shape {
public:
Circle* clone() const;
Circle* create() const;
...
};

--- Non-Covariant Return Types ---
// Use of this class requires no casts.
class Circle : public Shape {
public:
Shape* clone() const;
Shape* create() const;
...
};

Your second class does in fact require casts.

Circle aCircle;
Circle* badClone = aCircle.clone(); // ERROR!!!!
Circle* goodClone = static_cast<Circle*>(aCircle.clone);
[snip]

OK.
But my second class requires no casts for sample in http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8
 
V

Victor Bazarov

red said:
Alex said:
Alf P. Steinbach said:
* Alex Vinokur:
Why do we need covariant return types in the "virtual constructor"
design pattern?
You don't absolutely /need/ them: they're a convenience feature to
avoid casts.
[snip]

--- Covariant Return Types ---
class Circle : public Shape {
public:
Circle* clone() const;
Circle* create() const;
...
};

--- Non-Covariant Return Types ---
// Use of this class requires no casts.
class Circle : public Shape {
public:
Shape* clone() const;
Shape* create() const;
...
};

Your second class does in fact require casts.

Circle aCircle;
Circle* badClone = aCircle.clone(); // ERROR!!!!
Circle* goodClone = static_cast<Circle*>(aCircle.clone);

Nit pick:

Circle* goodClone = static_cast<Circle*>(aCircle.clone());

(you'd forgotten parentheses after 'clone')
Note that dynamic_cast would also be acceptable (assuming a virtual
function somewhere).

Why assuming? Why somewhere? Covariant return types _require_ the
function to be virtual, don't they?
The first class requires no such casts, and since upcasts to the base
class (Shape) do not require a cast either, that method is preferable.

V
 
J

Jakob Bieling

Alex Vinokur said:
Alex Vinokur wrote:
* Alex Vinokur:
Why do we need covariant return types in the "virtual constructor"
design pattern?
You don't absolutely /need/ them: they're a convenience feature to
avoid casts.
[snip]

--- Covariant Return Types ---
class Circle : public Shape {
public:
Circle* clone() const;
Circle* create() const;
...
};

--- Non-Covariant Return Types ---
// Use of this class requires no casts.
class Circle : public Shape {
public:
Shape* clone() const;
Shape* create() const;
...
};
Your second class does in fact require casts.

Circle aCircle;
Circle* badClone = aCircle.clone(); // ERROR!!!!
Circle* goodClone = static_cast<Circle*>(aCircle.clone);
OK.
But my second class requires no casts for sample in
http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8

The sample should have made use of the feature provided by covariant
return types. But it did not, that is why you do not need a cast. Change
the userCode function to:

void userCode(Circle& c)
{
Circle* s2 = c.clone();
Circle* s3 = c.create();
...
delete s2; // You need a virtual destructor here
delete s3;
}

And you will need a cast somewhere unless you use covariant return
types.

regards
 
A

Alex Vinokur

Jakob Bieling said:
The sample should have made use of the feature provided by covariant
return types. But it did not, that is why you do not need a cast. Change
the userCode function to:

void userCode(Circle& c)
{
Circle* s2 = c.clone();
Circle* s3 = c.create();
...
delete s2; // You need a virtual destructor here
delete s3;
}
[snip]

I think, if we use userCode(Circle&) instead of userCode(Shape&) it is not a virtual constructor.
 
J

Jakob Bieling

Alex Vinokur said:
Jakob Bieling said:
The sample should have made use of the feature provided by
covariant return types. But it did not, that is why you do not need
a cast. Change the userCode function to:

void userCode(Circle& c)
{
Circle* s2 = c.clone();
Circle* s3 = c.create();
...
delete s2; // You need a virtual destructor here
delete s3;
}
[snip]

I think, if we use userCode(Circle&) instead of userCode(Shape&)
it is not a virtual constructor.

Right, but it makes use of covariant return types. Obviously, you
have to know the returned type at compile-time, so the virtual
construction mechanism is not used. You use one *or* the other. But
covariant return types make it possible to use both (in different
places) with the same code without the need for a cast.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top