Pass cast ref-to-pointer from base to derived type

61 views Asked by At

I have a situation where I'm getting a reference to a pointer as a return type from a function. The reference is important, because I need the calling code to be able to modify the pointer.

But I also want to be able to return that through another function, this time cast to a ref-to-pointer of a derived type. This is causing a compiler error that I can't quite figure out how to get around.

This program shows what I'm trying to do:

#include <iostream>

class Foo
{
    public:
        virtual ~Foo() {}
};

class Bar : public Foo
{
    public:
        virtual ~Bar() {}
        void doit() { std::cout << "Done" << std::endl; }
};

class Container
{
    public:
        void setFoo(Foo *f) { _foo = f; }
        Foo*& getFoo() { return _foo; }

        Foo *_foo;
};

class OtherContainer : public Container
{
    public:
        void setBar(Bar *b) { setFoo(dynamic_cast<Foo*>(b)); }

        Bar*& getBar() 
        {
            return static_cast<Bar*&>(getFoo());
        }
};


int main(int argc, char *argv[])
{
    OtherContainer container;
    Bar bar;
    Bar otherBar;

    container.setBar(&bar);
    
    container.getBar() = &otherBar;

    return 0;
}

The compiler is returning this error:

g++     refptr.cc   -o refptr
refptr.cc: In member function ‘Bar*& OtherContainer::getBar()’:
refptr.cc:41:20: error: invalid ‘static_cast’ from type ‘Foo*’ to type ‘Bar*&’
   41 |             return static_cast<Bar*&>(getFoo());
      |                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
make: *** [<builtin>: refptr] Error 1

I've tried changing the static cast to just <Bar*> instead of <Bar*&> but then it says it "cannot bind non-const lvalue reference of type ‘Bar*&’ to an rvalue of type ‘Bar*’," which makes sense to me (and I didn't really expect that to work).

Is there even a way to achieve what I'm trying to do?

1

There are 1 answers

11
Daniel Engel On

After some fooling around, I found that a way to get this to work is to turn the return value from the inner getter into a pointer. Then reinterpret_cast that and dereference for the return to the outer getter:

class OtherContainer : public Container
...
    Bar*& getBar() 
    {   
        Foo **ptr = &getFoo();
        return *reinterpret_cast<Bar**>(ptr);
    }
...

As explained in my answer to Passer By's comment, I can do this /only/ because (within the full context of what I'm making) I absolutely know that the stored pointer to the base class (Foo) is really a pointer to the derived class (Bar).

Edit

With help from commenters, I landed on a different solution. I want as much of type-checking on the way in as reasonable, with natural type presentation on the way out. I've modified my example code to be a little closer (contextually) to what I'm going for (e.g., a templated derived class). I am grateful for the critiques offered so far; thank you!

#include <iostream>
#include <utility>

class Foo
{
    public:
        virtual ~Foo() {}
};

class Bar : public Foo
{
    public:
        virtual ~Bar() {}
        void doit() { std::cout << "Done" << std::endl; }
};

class Container
{
    public:
        void set(Foo *f) { setPtr(f); }
        Foo*& get()
        {
            void **ptr = &getPtr();
            return *reinterpret_cast<Foo**>(_foo);
        }

        void checkAs(void *p)
        {
            std::cout << ((_foo == p) ? "it works" : "it fails") << std::endl;
        }


    protected:
        void setPtr(void *p) { _foo = p; }
        void*& getPtr() { return _foo; }

    private:
        void *_foo;
};

// C is expected to be derived from Foo
template <class C>
class OtherContainer : public Container
{
    public:
        // hide, not override (would need to continue pattern through subclasses...anoying)
        void set(Foo *f)
        {
            C *c = dynamic_cast<C *>(f);
            if (c == nullptr)
            {
                // don't store - not a C *
                return;
            }

            setPtr(c); 
        }

        void set(C *c) { setPtr(c); }

        // hide, not override
        C*& get() 
        {
            void **ptr = &getPtr();
            return *reinterpret_cast<C**>(ptr);
        }
};

int main(int argc, char *argv[])
{
    OtherContainer<Bar> container;
    Bar bar;
    Bar otherBar;

    container.set(&bar);
    container.get() = &otherBar;
    container.checkAs(&otherBar);

    return 0;
}

The template class OtherContainer is intended to be the outermost exposed class of the framework that this would be a part of. I think this approach should always work intuitively, regardless of compiler implementation...(or will it?)

Edit #2

I want to thank everybody who took the time to comment on my question and proposed self-answer. I am a stubborn badger when it comes to giving up on an idea, but y'all have convinced me that this particular idea won't work the way I currently have it conceived. I need to find a different way to accomplish what I'm after. Thank you all.