3

Possible Duplicate:
Conversion between Derived** to Base**

I'm getting back into C++ after several years of mostly Python, and am hitting up against a strongly-typed wall. I think I have a good handle on basic polymorphism and type-casting between pointers of base and derived classes (e.g. Can a pointer of a derived class be type cast to the pointer of its base class?), but here's a stumper: why can't I assign a pointer to a pointer to a derived class to a p-to-p to it's base class?

For more context (and perhaps tips on doing this less pythonishly), here's a reduced version of what I'm trying to do. I want to have a list of pointers to objects (derived from a single class) and a string identifying them (i.e. a map<string, obj*>). A set of components will then pass a list of identifying strings and locations to store a pointer to the corresponding object (i.e. a map<string, obj**>). I should then be able to find the appropriate object by its string id, and fill in the appropriate pointer for subsequent use by the component. Simplified code to do this is

#include <map>
#include <string>
using namespace std;

class base
{
};

class derived: public base
{

};

typedef map<string, base**> BasePtrDict;
typedef map<string, base*> BaseDict;


int main(int argc, char* argv[])
{
    base b1, b2;
    derived d;
    derived* d_ptr;

    BaseDict base_dict;
    base_dict["b1"] = &b1;
    base_dict.insert(make_pair("b2",&b2)); // alternate syntax
    base_dict["d"]= &d;

    BasePtrDict ptr_dict;
    ptr_dict["d"] = &d_ptr;

    for (auto b = ptr_dict.begin(); b != ptr_dict.end(); b++)
        *(b->second) = base_dict[b->first];

    return 0;
}

This runs into a compile error at ptr_dict["d"] = &d_ptr;. Why? In the C++ paradigm, what should I be doing? Do I really need to do ugly (unsafe?) reinterpret_cast<base>() everywhere?

Community
  • 1
  • 1
Bismark
  • 31
  • 1
  • 4
  • Thanks all for helpful references and examples. Definitely makes sense now, I'll have to think about how to do this elegantly while maintaining strong typing ala C++. – Bismark Jul 12 '12 at 09:00

7 Answers7

3

You're losing the necessary information to be able to cast a base * to a derived *.

Consider the case where derived inherits from multiple base classes, so a cast needs to adjust the pointer value. Then derived *pd = static_cast<derived *>(pb) for a pointer-to-base pb will automatically apply the pointer adjustment.

However derived *pd; base **ppb = &pd; *ppb = *pb will fail to apply the pointer adjustment, so this cannot be legal.

What you should be doing is:

base *b_ptr;
... // existing code
derived *d_ptr = static_cast<derived *>(b_ptr);
ecatmur
  • 152,476
  • 27
  • 293
  • 366
2

Consider what would be enabled if it were permitted to treat a derived** as a base**:

class base
{
};

class derived: public base
{
public:
    int boom;
}


void light_the_fuse( base** pb)
{
    *pb = new base;
}


int main()
{
    derived* pd = NULL;

    light_the_fuse( &pd);

    pd->boom = 42;  // uh oh, `pd` points to a `class base`...

    return 0;
}
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
1

In order to do a child-to-base conversion the types need to be related. For derived** to base** this implies that derived* is a child of base* which is clearly not true as they're orthogonal pointer types (that happen to point to related classes).

You should be able to solve this with a static_cast rather than reinterpret_cast though:

ptr_dict["d"] = &static_cast<base*&>(d_ptr);
Mark B
  • 95,107
  • 10
  • 109
  • 188
0

The direct cast derived* to base* is ok, but that's not what you're attempting. You're attempting to do derived** to base**, which isn't an implicit cast. That aside, may I ask why? I'd even go ahead and ask whether you actually need pointers, but double pointers?

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
0

It will be much simpler to replace

derived* d_ptr;

with

base* d_ptr;

and use a cast anytime when you may need to apply the exact type of d_ptr but of course the principles of object orientation say that such a case should be quite rare.

jszpilewski
  • 1,632
  • 1
  • 21
  • 19
0

This is almost similar to Why do I need a reinterpret_cast to convert Fred ** const to void ** const?

Pointer to two different object types are basically incompatible. But, there are exceptions

  • Derived* to Base*... Derived and Base are related and therefore, conversion is allowed (after typecasting to Base*, the pointer would actually be pointing to an object of Base type
  • T* to void* - allowed (see the link above)

but there is no reason why D** should be allowed to be converted to B**, because B* and D* are essentially different types.

Community
  • 1
  • 1
Chethan
  • 905
  • 1
  • 10
  • 18
0

You cannot and should not assign a Derived** to a Base** because it is unsafe. If the cast was permitted, then it would allow Derived* to point to types that are not Dervied types. Consider the following:

class Base {};
class Derived: public Base {};
class Other: public Base {};

...

Derived derived;
Derived* derived_p   = &derived;   // derived_p points to derived.
Derived** derived_pp = &derived_p; // derived_p points to derived.
                                   // derived_pp points to derived_p.
Base** base_pp = derived_pp;       // derived_p points to derived.
                                   // base_pp points to derived_p.

Other other;
Other* other_p = &other;           // other_p points to other.

*bpp = op;                         // base_pp points to derived_p.
                                   // derived_p points to other.

If the above code was valid, it would allow Derived* derived = new Other().

Unfortunately, I am not exactly sure what is trying to be accomplished, so I cannot provide an alternative solution.

Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169