views:

1539

answers:

5

Hi, does anyone know why this gives a compiler error ? I tried VS 2005 and Codewarrior:

class Parent {
   protected:
      int m_Var;
   public:
      Parent() : m_Var(0) {}
      virtual ~Parent() {}
      void PubFunc();
};

class Child : public Parent {
   protected:
      bool m_Bool;
   public:
      Child() : m_Bool(false) {}
      virtual ~Child() {}
      void ChildFunc();
};

void RemoveObj(Parent *& ppObj)
{
   delete ppObj;
   ppObj = 0;
}

int main()
{
   Parent* pPObj = 0;
   Child*  pCObj = 0;
   pPObj = new Parent();
   pCObj = new Child();

   RemoveObj(pPObj);
   RemoveObj(pCObj);
   return 1;
}

Visual studio says:

refptr.cpp(33) : error C2664: 'RemoveObj' : cannot convert parameter 1 from 'Child *' to 'Parent *&'

Thanks

+13  A: 

The ppObj parameter to RemoveObj is a reference to a Parent*. What if the the RemoveObj() method replaced the pointer with a pointer to a new Parent object? When the method returned your the pCObj Child* would not be pointing to a Child object any more.

Michael Burr
Nice reasoning. +1 :)
Johannes Schaub - litb
+3  A: 

From the C++ standard (1998)

Except in the context of an initialization by user-defined conversion (13.3.1.4, 13.3.1.5), a well-formed implicit conversion sequence is one of the following forms: —a standard conversion sequence(13.3.3.1.1), -a user defined...

13.3.3.1.1

At most one conversion from each category is allowed in a single standard conversion sequence

So c++ can NOT convert implicitly two times in a row: from pointer to pointer and then again from pointer.

To clear this up consider such declaration of the RemoveObj

void RemoveObj(Parent ** ppObj)

And you will see this error

error: invalid conversion from 'Child**' to 'Parent**'

You have to use explicit conversion like

   RemoveObj((Parent**)&pCObj);
   RemoveObj((Parent*&)&pCObj);

or have to change

void RemoveObj(Parent *& ppObj)

to

void RemoveObj(Parent * ppObj)

or to

template <typename T>
void RemoveObj(T *& pObj)
{
   delete pObj;
   pObj = 0;
}
Mykola Golubyev
This doesn't answer why it is a complier error, but it is what it should be changed to be. The reference to the pointer is not necessary to do what the method is doing.
Brian ONeil
I've adder reference to the standard.
Mykola Golubyev
A: 

This is not authoritative, but I believe the problem is the polymorphic nature of C++ classes does not extend to their pointers; what you are expecting to be done here is for a Child * to be cast to a Parent *; while you can cast a Child to a Parent, you cannot cast the pointer reference. That is, the classes are polymorphic, but the pointers to the classes are not when taken as references. This is for the reason that Michael Burr gives above; the Child * implies a certain memory structure that the Parent * does not.

McWafflestix
conversion between pointers are possible. the conversion however returns an rvalue temporary. which is the reason it can't be passed to a non-const reference.
Johannes Schaub - litb
The problem is from the reference, not the pointers. You can pass the pointers interchangeably, but not a reference to a pointer of a different type pointer.
Brian ONeil
I think you were talking about conversions of lvalues to lvalues w.r.t Child* to Parent*. You are right such a conversion isn't possible. But your post sounds like you are implying that Child* to Parent* is disallowed (it isn't). Didn't downvote, just helping you figure out why they could have downvoted you. (if they only told you...)
Johannes Schaub - litb
@litb: thx for the clarification on the interpretation; edited to make it a bit clearer.
McWafflestix
You can do this experiment: void f(Parent * const you can call it with f(pChild); because the result of the implicit pointer conversion to Parent* can be bound to the parameter p: it's a non-const reference. Since we are not operating "polymorphic" here (we are not saying "the argument Child* pointer is the parameter Parent* pointer") but rather on a copy, this will work fine. I believe this is what you meant.
Johannes Schaub - litb
A: 

ppobj is the reference for the pointer. *ppobj dereferences what the variable points to, so you get the variable the pointer.

Since the dereference is not of the correct type you are seeing the error.

AndrewB
A: 

A pointer to a reference allows the pointer value to change in the function. As noted by Michael Burr there would be the potential to assign an incorrect class reference and return it. Imagine your entire program incorrectly using *pchickens as *peggs :)

I thought it was worth adding (although not explicitly what you asked): My preference for a polymorphic implementation is to move common functions inside as a methods. If they all share a function, simple add it the base class.

Then either way you could simply call Foo->Bar() and achieve the desired result. But for the specific implementation example you give, simply delete Foo would call the appropriate destructor.

Mark Essel