4

I'm having a peculiar issue. I thought I knew a decent amount of C++ but I must be overlooking some nuance?

I've got some templated classes, and essentially all that is happening is based on some condition, an object is being moved from one std::list to another.

The method of focus here is the doAndGet( T *t ). My intent is for t to come in as NULL (or whatever, I really don't care, but it should be NULL or nullptr, and if I end up moving a T element *t from myList to myOtherList, I want T *t to point to the item that was moved.

Now maybe there is something about std::list that I'm not aware of, but when stepping through my code, t gets assigned properly and—originally I thought after the method returned, but later found out that—right before the ret instruction, argument T *t gets set back to NULL

Can any guru shed some light on this issue? It's a real head-scratcher for me...

template<typename T, typename C>
class Base : IsDerivedFrom<T, C>
{
public:
    typedef std::list<T*> SpecialList;
    virtual bool doAndGet( T* t ) = 0;

protected:

    SpecialList myList;
};

template<typename T>
class Derived : public Base<T, Other>, public Other
{
public:
    Derived( void );
    virtual ~Derived( void );

    bool doAndGet( T* t );

protected:

    typename Base<T, Other>::SpecialList myOtherList;

};

template<typename T>
bool Derived<T>::doAndGet( T *t )
{
    // Nothing to process
    if (myOtherList.empty()) {return false;}

    t = (*myOtherList.begin());
    myOtherList.pop_front();

    if (true/* some condition needs to be moved */)
    {
        t->status = 1;
        this->myList.push_back(t);
        return true;
    } else
    {
        // Something bad happened...
        delete t;
        t = nullptr;
        return false;
    }
}

// Use... (pretend I defined MyOtherClass somewhere, not important)
Derived<MyOtherClass> derivedInstance;

MyOtherClass *t = nullptr;
if ( derivedInstance.doAndGet(t) )
{
    // ... Here I should expect `t` to be not `NULL` and not `nullptr`
    // This however, is not ever the case in my testing...
}
Volte
  • 1,905
  • 18
  • 25
  • `doAndGet` cannot modify the `t` passed in because it's passed by value. If your assignment of `t` inside of `doAndGet` is supposed to have a side-effect, take `t` by reference (e.g. T*&). – Peter Huene Jun 10 '14 at 04:45
  • your base class is missing virtual destructor (its not the issue though). – quantdev Jun 10 '14 at 04:46
  • 1
    How did you learn templates, multiple inheritance, the curiously-recurring template pattern without finding out about pass-by-value :) – M.M Jun 10 '14 at 04:47

2 Answers2

7

If you want to pass in a variable to a function and have any changes within that function to be reflected back to the caller, you need to use a reference.

C++, like C, is pass by value, where a function only gets a copy of a variable. C++ has references to allow pass by reference.

In other words, your function signature should be:

bool doAndGet( T* &t );

to state that t is a reference to a T*, allowing changes to reflect back to the original pointer in the caller.

A simpler example follows:

#include <iostream>
static void change (int a, int &b) { a = b = 0; }
int main() {
    int a = 42, b = 42;
    change (a, b);
    std::cout << a << ' ' << b << '\n';
    return 0;
}

This outputs 42 0 because the a passed in is pass by value and changes are not reflected back to the caller. The b is passed in as a reference so changes are reflected back.

paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • I'm passing a pointer because I have nothing to give the function, I want the pointer to point to a valid object if it returns true. – Volte Jun 10 '14 at 04:46
  • Boom. You nailed it. Thank you! – Volte Jun 10 '14 at 04:49
  • @Volte, that's right, but the _pointer_ cannot be changed unless you have a reference. The thing the pointer points to can be changed (and this is how the reference-deficient C does pass by reference) but your function wants to change the actual pointer. – paxdiablo Jun 10 '14 at 04:50
  • I have one more semi-related question. In my code elsewhere, I have another similar method that has `read( T *t, SomeClass &s )` to which I pass as I was in my original question, a `T *p = NULL` and it sets `p` properly upon return... Why? Are there certain circumstances under which this works? – Volte Jun 10 '14 at 05:04
  • Actually, I take that back. What I thought was working actually wasn't... I updated those old functions with my new found knowledge (thanks to you guys) and it's all working splendidly! – Volte Jun 10 '14 at 05:10
1

doAndGet passes its parameter by value. So the parameter in the calling function is not changed.

Simpler example of the same thing:

void func(int x)
{
    ++x;
}

int main()
{
    int y = 5;
    func(y);
    std::cout << y << "\n";   // outputs 5
}
M.M
  • 138,810
  • 21
  • 208
  • 365
  • But I'm passing a pointer... – Volte Jun 10 '14 at 04:47
  • You're passing the pointer by value. `t` in the function and `t` in the caller are separate variables. (Both pointers point to the same thing, but the pointers themselves live in different locations) – M.M Jun 10 '14 at 04:48
  • so I should pass a pointer to the pointer...? – Volte Jun 10 '14 at 04:48
  • @Volte Yes, but the pointer itself is being passed by value. You can modify the item being pointed at, but you cannot reseat the pointer and have that change be visible outside the function. Change the parameter to `T *& t` to have it work as you expect. – Praetorian Jun 10 '14 at 04:49
  • Yeah, or in C++ you can pass by reference, e.g. `bool Derived::doAndGet( T * &t )` – M.M Jun 10 '14 at 04:49
  • Oh man now my head is spinning. Ok I think I'm starting to understand... I always thought pointers were... pointers, and if I assigned it something else we'd be good. Wow I might need to take a whole-nother look at my library... – Volte Jun 10 '14 at 04:50
  • @MattMcNabb Is there a place that can explain this (well, to me anyway) nuance? I thought I had a solid understanding of pointers, references and values... but now values of pointers and references to pointers... – Volte Jun 10 '14 at 04:50
  • Other than reading a book... just bear in mind that a pointer is still a variable - it just holds an address , as opposed to say an `int` which holds a number. There's no essential difference between your code example and mine. Keep it clear in your head the distinction between *the pointer* and *the thing being pointed at* - avoid mentally thinking of `t` as a handle on the thing being pointed at. – M.M Jun 10 '14 at 04:53
  • [This thread](http://stackoverflow.com/questions/21215409/does-c-pass-objects-by-value-or-reference?rq=1) has a bit more explanation – M.M Jun 10 '14 at 04:54
  • Thanks @MattMcNabb! Very helpful. I would have selected your answer, but @paxdiablo's was technically more to the point, despite how helpful and informative you've been. :) – Volte Jun 10 '14 at 05:11