views:

165

answers:

4

Just stumbled onto this problem. (title says it all)

Let's say I have a struct

struct Foo {
    void bar () {
       do_baz(this);
    }
    /* See edit below
    void do_baz(Foo*& pFoo) {
       pFoo->p_sub_foo = new Foo; // for example
    }
    */

    Foo* p_sub_foo;
}

GCC tells me that

temp.cpp: In member function ‘void Foo::bar()’:
temp.cpp:3: error: no matching function for call to ‘Foo::do_baz(Foo* const)’
temp.cpp:5: note: candidates are: void Foo::do_baz(Foo*&)

So, how do I convert what is apparently a const Foo* to a Foo*&?

EDIT: I didn't use a very good example. do_baz should read

void do_baz(Foo*& pFoo) {
    if (pFoo == NULL) {
        pFoo = new Foo;
        return;
    }
    //other stuff
    do_baz(pFoo->p_sub_foo);
    //more stuff
}
+6  A: 

You can't.

Firstly, this is not necessarily a const Foo *. this would be a const Foo * is a const method of the class Foo. In a non-const method this is just Foo *. (Actually your error message mentions Foo* const. Where did you see const Foo *?)

Secondly, and more importantly, this is not an lvalue. You can't have a pointer to this. You can't have a non-constant reference to this. The only thing that you can have is a const reverence to this, i.e. a reference of type Foo *const &.

It (Foo *const &) will work in your case.

void do_baz(Foo* const& pFoo) { 
   pFoo->p_sub_foo = new Foo;
} 

But I don't see the point of all this. Just declare a normal Foo * pointer as parameter for your do_baz method

void do_baz(Foo* pFoo) { 
   pFoo->p_sub_foo = new Foo;
} 

and get the same result. What do you think you need that reference for?

EDIT: Taking into account your edit, what you are trying to do cannot be done with a single do_baz function, since in the first call you'd potentially (semantically) attempt to modify this, which is impossible (even if the modifying code will never be executed in practice). Whether you want it or not, you can't have a non-const reference to this, even if you don't intend to write anything through it. You'll probably have to implement the very first call with a different function

void do_baz(Foo*& pFoo) { 
  if (pFoo == NULL) { 
    pFoo = new Foo; 
    return; 
  } 
  //other stuff 
  do_baz(pFoo->p_sub_foo); 
  //more stuff 
} 

void do_baz_root(Foo* pFoo) { 
  assert(pFoo != NULL);
  //other stuff 
  do_baz(pFoo->p_sub_foo); 
  //more stuff 
} 

and then make the first call as

void bar() {
  do_baz_root(this);
}
AndreyT
The reference would be because in my actual application, I would be allocating `pFoo = new Foo;` Guess I didn't quite represent that well...
Austin Hyde
@Austin Hyde: Sorry, but that just makes no sense from C++ point of view. You can't change `this`. `this` is not changeable. What were you trying to implement by that code that attempted to change `this`?
AndreyT
Fixed the question. I wouldn't be trying to modify `this`, since I would be checking for `NULL`. I had a "doh" moment while writing the question.
Austin Hyde
@Austin Hyde: I don't get it. Your new example checks `this` for null and then *attempts to modify* `this` through the reference. It makes no sense on more levels than one. `this` cannot be modified. And `this` cannot be null, so checking it for null doesn't make sense. An attempt to call a method through a null pointer leads to undefined behavior anyway.
AndreyT
Well, that's part of the problem. But I think you missed the recursive call. And I wouldn't be modifying `this` because if it got so far as to call `do_baz(this)` then `pFoo` would NOT be NULL - that is: `do_baz` **does not** directly modify `this` - *only* if you call `do_baz(this)`.
Austin Hyde
@Austin Hyde: See my edit. As for modifying `this`: when you create a non-const reference to `this`, it already opens all doors to modifying `this`, albeit indirectly. I understand now that you are not actually trying to modify `this`, yet what you do is semantically illegal.
AndreyT
A: 

The keyword this is not an lvalue so this can't be assigned to/changed (regardless of whether what it points to is const or not). In other words, you might be able to change what this points to, but you can't change the value of this itself. The C++ standard 9.3.2 "The this pointer":

In the body of a nonstatic (9.3) member function, the keyword this is a non-lvalue expression

Only const references can bind to non-lvalue objects, so you'd need to bind it to a Foo* const& if you want to bind the this pointer to a reference.

Change the signature of the function to:

void do_baz(Foo* const& pFoo);
Michael Burr
+3  A: 

Give the variable a name, and then you will have a pointer reference type:

void bar () {
   Foo* me = this;
   do_baz(me);
}

I should also point out that your do_baz function isn't making use of the fact that its parameter is a reference (you are not assigning to the pointer, itself, only to what is being pointed to by the pointer). Consequently, it really makes more sense to change the type of the parameter to Foo* instead of Foo*&, or to make it a Foo&, in which case you would use dot (.) instead of arrow (->) when dereferencing the parameter's member.

Edit
Your new version of do_baz now makes use of the fact that the parameter is a reference. The solution above (simply using a named pointer) will still work for your new version of the problem. That said, I would advise against what you are doing. It seems you are trying to insert an element at the end of a linked list...

Firstly, I would advise that if you are implementing a linked list that you maintain not only a pointer to the first node in the list, but also a pointer to the last node in the list at all times, so that insertion at the end of the list may be performed in constant-time. If, however, that is not a possibility, I would nevertheless advise you to use an iterative implementation rather than a recursive one as it is cleaner and simpler. It would look like:

Foo* current=this;
while (current->next != NULL){
    current=current->next;
}
current->next = new Foo;
Michael Aaron Safyan
A: 

You don't.

this is a Foo* const, meaning it is a const pointer to a non-const Foo. Your reference is non-const, so the correct declaration would be a Foo* const &.

But it doesn't make any sense to do this, so don't.

greyfade