3

I have a derived class and a base class that both override the same virtual function. How can I pass some other function a pointer or reference to an instance of the derived class, but still call the base class's version of the virtual function?


I appear to have precisely the opposite problem from this question. I effectively want to slice my derived object so that it looks like a base object (assuming I can do so safely), but can't figure out how to do it.

I have a large set of data that would be expensive to copy, so I instead pass around a lightweight view object that allows iteration. I have an interface that various datasets inherit to advertise that they can provide a view:

class data_interface
{
    virtual view get_view() = 0;
};

There's a base class that owns one of these datasets, and can provide a view to it on request:

class base_owns_data : public data_interface
{
public:
    view get_view() override
    {
        return view(d);
    }

private:
    data d;
};

That class works fine in isolation. My application does things like pass it by reference to free functions, in order to get a more complex view into the data:

view some_process(base_owns_data &b)
{
    auto working_view = b.get_view();
    // Do a bunch of things to filter the view
    return working_view;
}

(Please note that the above is stripped down for the MCVE - here, it would make more sense to directly take a view parameter, but in context I'm combining several datasets and one of them needs to own the storage rather than just having view to it)

The problem appears when I try to extend the base dataset with another that needs to do some extra processing. Here's a derived class:

class derived_owns_data : public base_owns_data
{
public:
    base_owns_data& as_base()
    {
        return *this;
    }

    view get_view() override
    {
        return some_process(this->as_base());   
    }
};

What I wanted to happen is that, since some_process is being passed a reference to base_owns_data, that base_owns_data::get_view() would be called in some_process.

What actually happens is that some_process still calls derived_owns_data::get_view(). This results in an infinite loop as those two functions call each other back and forth.

I'm not sure what to do - I can't remove virtual from the interface, or the interface becomes useless. I don't want to copy the data, either. But I can't figure out how to force base_owns_data::get_view() to be called on a reference of type derived_owns_data.

I tried passing a pointer instead of a reference, but that still seems to cause an infinite loop.

How can I safely make the derived class appear to be a base class in some_process?


Full MCVE (or on ideone):

struct data
{
    char c[100];
};

class view
{
public:
    view(data d) :
        first(&d.c[0]),
        last(&d.c[100])
    {

    }
private:
    char * first;
    char * last;
};

class data_interface
{
    virtual view get_view() = 0;
};

class base_owns_data : public data_interface
{
public:
    view get_view() override
    {
        return view(d);
    }

private:
    data d;
};

view some_process(base_owns_data &b)
{
    return b.get_view();
}

class derived_owns_data : public base_owns_data
{
public:
    base_owns_data& as_base()
    {
        return *this;
    }

    view get_view() override
    {
        return some_process(this->as_base());   
    }
};


int main() {
    derived_owns_data d;
    d.get_view();
    return 0;
}
Bear
  • 345
  • 4
  • 16
  • 1
    You can't slice an existing object. That would imply changing it's type. Object slicing occurs when copying from a derived type to a base type. – François Andrieux Feb 15 '18 at 21:29
  • Perhaps this could be solved with the [Curiously Recurring Template Pattern](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)? – callyalater Feb 15 '18 at 21:30
  • 1
    Regarding polymorphism, the object's actual type is distinct from the type used to refer to it. The goal of using `virtual` functions is that it the derived type behaves correctly even if it's referred to by a pointer or reference to one of it's base types. Casting the reference or pointer to a base type doesn't alter which `virtual` function will get called as that would entirely defeat the purpose of polymorphism. – François Andrieux Feb 15 '18 at 21:32

1 Answers1

3

You have to explicitly call overriden function from base class:

view some_process(base_owns_data &b)
{
    auto working_view = b.base_owns_data::get_view();
    // Do a bunch of things to filter the view
    return working_view;
}
bartop
  • 9,971
  • 1
  • 23
  • 54
  • Never occurred to me to qualify a call on a reference with its own (apparent) type - thanks! – Bear Feb 19 '18 at 12:59