views:

248

answers:

5

I have the following contrived example (coming from real code):

template <class T>
class Base {
public:
 Base(int a):x(a) {}
    Base(Base<T> * &other) { }
    virtual ~Base() {}
private:
 int x;
};

template <class T>
class Derived:public Base<T>{
public:
  Derived(int x):Base<T>(x) {}
  Derived(Derived<T>* &other): Base<T>(other) {}

};


int main() {
 Derived<int> *x=new Derived<int>(1);
 Derived<int> y(x);
}

When I try to compile this, I get:

1X.cc: In constructor ‘Derived<T>::Derived(Derived<T>*&) [with T = int]’:
1X.cc:27:   instantiated from here
1X.cc:20: error: invalid conversion from ‘Derived<int>*’ to ‘int’
1X.cc:20: error:   initializing argument 1 of ‘Base<T>::Base(int) [with T = int]’

1) Clearly gcc is being confused by the constructors. If I remove the reference from the constructors, then the code compiles. So my assumption is that something goes wrong with up-casting pointer references. Can someone tell me what is going on here?

2) A slightly unrelated question. If I were to do something horrendous like "delete other" in the constructor (bear with me), what happens when someone passes me a pointer to something on the stack ?

E.g. Derived<int> x(2);
     Derived<int> y(x);

where 

 Derived(Derived<T>*& other) { delete other;}

How can I make sure that pointer is legitimately pointing to something on the heap?

+2  A: 

You make sure that pointer points to something on the heap by writing that in your documentation and relying on the caller to abide by that. If whoever calls your constructor passes a stack pointer, all bets are off, and it's not your fault - you can try to catch the problem early, but no guarantees.

That's how the standard library works - often it'll catch obvious errors, but it's not required to, and it's up to the caller to make sure they're not doing anything stupid.

Anon.
A: 

Your x variable is not a pointer, it should be if you want to assign a new Derived<int> to it.

As for deleting things on the stack, don't do it. There is no way to tell whether you have been passed the address of something on the stack or on the heap (indeed, the C++ standard doesn't even acknowledge the existence of a stack). The lesson here is that you shouldn't be deleting things that you don't own, especially if you have no way of telling where they came from.

Peter Alexander
Sorry corrected my example.
on one hand you corrected your example, on the other hand it was correct, because you can initialize `Derived<int>` from pointer (or, at least you intend to be able to) with the second constructor.
Michael Krelin - hacker
A `Derived<int>*` can be taken as constructor argument. So your first phrase is not correct...
xtofl
It may be legally allowed, but it was almost certainly not what he was trying to do, and would be terribly bad practice if it were. I never claimed that what he was doing was illegal.
Peter Alexander
A: 

Not sure why do you want reference to the pointer. Why not

Base(Base<T> * other) { }

and

Derived(Derived<T>* other): Base<T>(other) {}

That should work.

And, like other answered, I don't think you can legitimately know whether the pointer is pointing into heap.

Edit: why can't one do what you're trying to: consider example:

Derived1<int> *x = new Derived1<int>
Base<int> **xx =&x;
Derived2<int> y;
*xx = &y;

Where Derived1 and Derived2 are different classes derived from Base? Would you think it's legitimate? Now that x of type Derived1* points to Derived2?

Michael Krelin - hacker
I do want reference to pointers. In the real code, I need to access and changed Derived's members in place.
But if you have pointer, you already can change Derived's members
Michael Krelin - hacker
You need reference to pointer to be able to change pointer itself. But that *should* not be possible, because it would allow you to set Derived* to some Base*, which is not Derived*.
Michael Krelin - hacker
You cannot have pointers to references.
Roger Pate
Roger, on the second thought it didn't make much sense to me either.
Michael Krelin - hacker
I wasn't clear. I want to change "other" to point to something elseor set to NULL. If I don't have a reference, I won't be ableto do that as I will be only working with a copy.
Roger: I have reference to a pointer. That is legal C++.
unknown: yes, references to pointers are fine (see my answer), pointers to references cannot happen.
Roger Pate
Unknown Google, I just explained to you why it wouldn't work. Casting pointer to pointer to Derived to pointer pointer to Base effectively provides you the way to cast pointer to Base to pointer to Derived. And do it you should need a brute force.
Michael Krelin - hacker
Roger was referring to my parenthetic remark which I edited out, because it failed to make sense even to myself.
Michael Krelin - hacker
+3  A: 
  1. You cannot convert a reference to a pointer to Derived to a reference to a pointer to Base. (Templates don't contribute to the issue here, so removed from my example below.)
  2. If you want to defer responsibility for a pointer, use a smart pointer type. Smart pointer types can represent the "responsibility to delete" that raw pointers cannot. Examples include std::auto_ptr and boost::shared_ptr, among many others.

Why you cannot upcast pointer references:

struct Base {};
struct Derived : Base {};
struct Subclass : Base {};

int main() {
  Derived d;
  Derived* p = &d;
  Derived*& d_ptr = p;

  Base*& b_ptr = d_ptr; // this is not allowed, but let's say it is

  Base b;
  b_ptr = &b; // oops! d_ptr no longer points to a Derived!

  Subclass s;
  b_ptr = &s; // oops! d_ptr no longer points to a Derived!
}

When you pass your 'other' parameter to the Base ctor, you're trying to do the same thing as b_ptr = d_ptr above.

Roger Pate
+6  A: 

Base<T> is a base type of Derived<T>, but Base<T>* is not a base type of Derived<T>*. You can pass a derived pointer in place of a base pointer, but you can't pass a derived pointer reference in place of a base pointer reference.

The reason is that, suppose you could, and suppose the constructor of Base were to write some value into the reference:

Base(Base<T> * &other) {
    Base<T> *thing = new Base<T>(12);
    other = thing;
}

You've just written a pointer to something which is not a Derived<T>, into a pointer to Derived<T>. The compiler can't let this happen.

Steve Jessop