Topic: Derived** cannot be converted implicitly to Base*const* . Why?


Author: "Silvio Bierman" <sbierman@idfix.nl>
Date: 2000/02/05
Raw View
Allowing it would introduce a hole in the type system:

struct A { };
struct B : A { };
struct C : A { };

B *pb = 0;
B**ppb = &pb;

A **ppa = ppb; /* this is not allowed, for good reasons... */

C *pc = new C;
A *pa = pc;
*ppa = pa;

/* At this point pb (which is a B*) points to the same C object pc points to
*/

Silvio Bierman

Niels Dekker wrote in message <389760B7.177D3E8A@NO_SPAM_PLEASEnki.nl>...
>(Sorry if this is a FAQ, but I have only found the answer for a
>pointer-to-NON-const-pointer-to-Base.)
>
>If Derived is a class that is public-derived from Base, why can't a
>Derived** argument be passed as Base*const* parameter?
>And when I have a function that works on an array of constant
>base-class-pointers, why can't I use it for an array of
>derived-class-pointers?
>
>// Example:
>class Person
>{
>public:
>  virtual std::string GetName(void) const = 0;
>};
>
>class Programmer: public Person
>{
>public:
>  std::string GetName(void) const;
>};
>
>void PrintNamesOfPersons(const Person* const ArrayOfPersons[]);
>Programmer* ArrayOfProgrammers[16];
>
>void Test(void)
>{
>  PrintNamesOfPersons(ArrayOfProgrammers); // Error, but why?
>}
>
>Can you give an example wherein conversion from Derived** to
>Base*const* is harmful?
>
>Thank you in advance...
>
>Niels Dekker
>ndekker "at" nki "dot" nl
>
>---
>[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
>[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
>[              --- Please see the FAQ before posting. ---               ]
>[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]
>


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Niels Dekker <ndekker@NO_SPAM_PLEASEnki.nl>
Date: 2000/02/06
Raw View
Thanks to all of you for showing how the conversion I asked for might
lead to trouble if "Derived" has multiple base classes.

So if C++ didn't support multiple inheritence, it could have allowed
the implicit conversion from Derived** to Base*const*. Or not?

Anyway, I have the same problem in ObjectPascal (Delphi 5), even
though this language doesn't support mutual inheritence for classes.
(My ObjectPascal procedure has a const array of Base as parameter, but
cannot be used for an array of Derived.)


Valentin Bonnard wrote:
>
> Use a Base* const & instead of Base* const*, and you'll get
> the `` conversion '' from Derived*.
>
Nice suggesion, thanks! Of course that doesn't really help me when I
try to pass an array of Derived* as argument for an array of
Base*const parameter...


Silvio Bierman wrote:
>
> Allowing it would introduce a hole in the type system:
>
> struct A { };
> struct B : A { };
> struct C : A { };
>
> B *pb = 0;
> B**ppb = &pb;
>
> A **ppa = ppb; /* this is not allowed, for good reasons... */
>
You're right, but my question is about disallowing conversion to a
pointer-to-a-const-pointer-to-base-class:
  A *const*ppa = ppb; // This is what I tries to do!


Have a good evening,

    Niels Dekker
    ndekker "at" nki "dot" nl

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 2000/02/07
Raw View
Niels Dekker wrote:

> So if C++ didn't support multiple inheritence, it could have allowed
> the implicit conversion from Derived** to Base*const*. Or not?

It _could_. It also could if we were ready to accept an
inefficient class representation, incompatible with the
one of C.

> Valentin Bonnard wrote:
> >
> > Use a Base* const & instead of Base* const*, and you'll get
> > the `` conversion '' from Derived*.
> >
> Nice suggesion, thanks! Of course that doesn't really help me when I
> try to pass an array of Derived* as argument for an array of
> Base*const parameter...

That's correct, it only works for single elements, not for arrays.

<ADVERTISING CLASS=exaggeration>Learn generic programming, the STL,
etc... and you won't ever have these problems.</ADVERTISING>

--

Valentin Bonnard

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Patrick Alberts" <Patrick@Alberts.org>
Date: 2000/02/09
Raw View
"Niels Dekker" <ndekker@NO_SPAM_PLEASEnki.nl> wrote in message
news:389760B7.177D3E8A@NO_SPAM_PLEASEnki.nl...
> If Derived is a class that is public-derived from Base, why can't a
> Derived** argument be passed as Base*const* parameter?
> And when I have a function that works on an array of constant
> base-class-pointers, why can't I use it for an array of
> derived-class-pointers?

As posted to comp.lang.c++ I have a similar problem. In the meantime, I've
used a rather crude solution, but it seems to work:

// first prepare our derived array of pointers
derived** ppDerived = new (derived*)[aNumber];
for( int i = 0; i < aNumber; ++i ) {
  ppDerived[i] = new derived;
}
...
// some code
...
// now we want to call function work( base** ppBase )
// so I just re-interpret the array:
base** ppBase = new (base*)[aNumber];
for( int i = 0; i < aNumber; ++i ) {
  ppBase[i] = (base*) ppDerived[i];
}
work( ppBase );
delete ppBase;
...
// some other code
...
// tidy up
for( int i = 0; i < aNumber; ++i ) {
  delete ppDerived[i];
}
delete [] ppDerived;

This seems to work, but it's not very elegant. Is this correct or do I do
something really dangerous here?

pat!



---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Ron Natalie <ron@sensor.com>
Date: 2000/02/11
Raw View

Patrick Alberts wrote:
>
>   ppBase[i] = (base*) ppDerived[i];

This shouldn't require a cast.  Mindless casting like this
hides the type of type safety checking that C++ wants to do for
you.

But in general you are correct, if you have an array of derived*
and you want to convert to array of base* you must go through
and convert each element to be safe.

> delete ppBase;

Needs to be delete[], but you knew that, didn't you.

You could always use vectors and the "copy" templated operator.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Niels Dekker <ndekker@NO_SPAM_PLEASEnki.nl>
Date: 2000/02/02
Raw View
(Sorry if this is a FAQ, but I have only found the answer for a
pointer-to-NON-const-pointer-to-Base.)

If Derived is a class that is public-derived from Base, why can't a
Derived** argument be passed as Base*const* parameter?
And when I have a function that works on an array of constant
base-class-pointers, why can't I use it for an array of
derived-class-pointers?

// Example:
class Person
{
public:
  virtual std::string GetName(void) const = 0;
};

class Programmer: public Person
{
public:
  std::string GetName(void) const;
};

void PrintNamesOfPersons(const Person* const ArrayOfPersons[]);
Programmer* ArrayOfProgrammers[16];

void Test(void)
{
  PrintNamesOfPersons(ArrayOfProgrammers); // Error, but why?
}

Can you give an example wherein conversion from Derived** to
Base*const* is harmful?

Thank you in advance...

Niels Dekker
ndekker "at" nki "dot" nl

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Valentin Bonnard <Bonnard.V@wanadoo.fr>
Date: 2000/02/02
Raw View
Niels Dekker wrote:

> (Sorry if this is a FAQ, but I have only found the answer for a
> pointer-to-NON-const-pointer-to-Base.)

It isn't frequently asked.

> If Derived is a class that is public-derived from Base, why can't a
> Derived** argument be passed as Base*const* parameter?

A Derived* isn't representation-compatible with a Base* (a T*
is representation-compatible with a const T*).

Use a Base* const & instead of Base* const*, and you'll get
the `` conversion '' from Derived*.

--

Valentin Bonnard


[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Ron Natalie <ron@sensor.com>
Date: 2000/02/02
Raw View


Dave Abrahams wrote:
>
>
> It's because the physical address of a base class need not be the same as
> that of a derived class. Multiple-inheritance will illustrate this the
> quickest. Try some experiments with
>
> struct Base { int base; };
> struct X { int x; };
> struct Derived : X, Base {};
>
> and you'll probably see what's going on immediately.

Yep, he has two problems here (I missed the second for the first).

1.  Can't convert
 T** to const T**.
 (Blows type safety see my other posting).
2.  Can't convert
 Derived** to Base**
 Only know how to convert Derived* to Base*
 (would have to changed *(Derived**) which would
        hose anything still treating it as Derived*.



[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Dave Abrahams <abrahams@mediaone.net>
Date: 2000/02/03
Raw View
in article 389760B7.177D3E8A@NO_SPAM_PLEASEnki.nl, Niels Dekker at
ndekker@NO_SPAM_PLEASEnki.nl wrote on 2/2/00 4:10 PM:

> If Derived is a class that is public-derived from Base, why can't a
> Derived** argument be passed as Base*const* parameter?
> And when I have a function that works on an array of constant
> base-class-pointers, why can't I use it for an array of
> derived-class-pointers?

It's because the physical address of a base class need not be the same as
that of a derived class. Multiple-inheritance will illustrate this the
quickest. Try some experiments with

struct Base { int base; };
struct X { int x; };
struct Derived : X, Base {};

and you'll probably see what's going on immediately.

-Dave

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: fjh@cs.mu.OZ.AU (Fergus Henderson)
Date: 2000/02/03
Raw View
Niels Dekker <ndekker@NO_SPAM_PLEASEnki.nl> writes:

>If Derived is a class that is public-derived from Base, why can't a
>Derived** argument be passed as Base*const* parameter?

The conversion from Derived* to Base* might require
a change in value or representation.  For example,
that is often necessary in cases where multiple inheritance
is involved.  To convert from a Derived* to a Base*, the compiler
may need to add the offset of the Base class within the Derived
class object.

This means that you can't just reinterpret the bits of a Derived*
as if it were a Base*.  So, regardless of the const, a pointer
to Derived* is not the same as a pointer to Base*.

>Can you give an example wherein conversion from Derived** to
>Base*const* is harmful?

#include <iostream>

struct Base1 { int x; };
struct Base { int y; };
struct Derived : Base1, Base { };

int main() {
 Derived d;
 d.x = 100;
 d.y = 42;
 Derived *pd = &d;
 Derived **ppd = &pd;
 Base *const*pcpb = (Base*const*) ppd;
 std::cout << (*pcpb)->y << std::endl; // undefined behaviour
     // typically outputs `100', not `42'.
}

--
Fergus Henderson <fjh@cs.mu.oz.au>  |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>  |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3        |     -- the last words of T. S. Garp.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Ron Natalie <ron@sensor.com>
Date: 2000/02/03
Raw View
This is in the C++ FAQ.   It's never safe to convert T** to const T**.
(Doesn't have anything to do with the Derived to Base part of the
conversion).

 T* tp;
 T** tpp = &tp;  // Legit.

 const T** ctpp = tpp;  // Presume this is legal (it's not).

 const T ct;
 *ctpp = &ct;  // legal:  const T* = const T*

 //  Note at this point we still have tp of type T*
 //  and it now has the value of &ct of type const T*

 *tp = T(); // BOOM, modified a const object
   // without having to force it with
   // a cast!


The way to do this is to change the ctpp definition to be:

 const T* const* ctpp = tpp; // Now legal.
 *ctpp = &ct;   // BOGUS! *ctpp is const.

Which allows you do what you probalbly want but elimiates type games.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: =?ISO-8859-1?Q?J=F6rg?= Barfurth <joerg.barfurth@germany.sun.com>
Date: 2000/02/03
Raw View
Am 02.02.00, 16:10:19, schrieb Niels Dekker
<ndekker@NO_SPAM_PLEASEnki.nl> zum Thema Derived** cannot be converted
implicitly to Base*const* . Why?:

> If Derived is a class that is public-derived from Base, why can't a
> Derived** argument be passed as Base*const* parameter?
> And when I have a function that works on an array of constant
> base-class-pointers, why can't I use it for an array of
> derived-class-pointers?

A Derived* need not have the same value representation as a Base*
pointing to its    Base    base class subobject. This is especially
obvious in case of multiple inheritance.
The only way a Derived** could be converted to a Base** (whatever
qualifiers are present) would have the result of the conversion
pointing at the same memory as the original pointer (in most
implementations a reinterpret_cast will do this).
There is no guarantee that the contents of that memory can be validly
interpreted as Base* much less that such interpretation would yield a
pointer to the    Base    base class subobject of the Derived object
originally pointed to.

> Can you give an example wherein conversion from Derived** to
> Base*const* is harmful?

 struct A { std::string s; virtual void foo(double); };
 struct Base { mutable int i; virtual double bar(char) const; };
 struct Derived : A, Base {};

 void f(Base const * const * ppb)
 {
  (**ppb).i = 42;
  (**ppb).bar('x');
 }

 void g(Derived** ppd)
 {
  f(ppd); // ERROR
  // f(reinterpret_cast<Base**>(ppd));
 }

Uncommenting the second line in g() considered harmful.
In my implementation the first line of f will overwrite internals of
the A::s string object in (**ppd).
The second line will damage the stack if A::foo() hasn't crashed
before that.

--
J   rg Barfurth
joerg.barfurth@germany.sun.com









---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]