0

In the following code I return a derived class CDerived by-value from a function returning its base class, CBase.

For example purposes, CDerived contains a _number field, and I understand that this is "sliced" off during its conversion to the by-value return type, CBase.

However, what confuses me here is that the virtual function Print somehow gets transmuted into its base-class version. In this case the v-table hasn't simply been sliced, it's been altered.

In my actual code _number does not exist, nor does any other data. I would like to be able to return a derived class only for it's v-table, which is declared by the base.

From this question I see that I can "work around" this particular issue by returning a function pointer (or I suppose crafting my own v-table), but why is the v-table being altered in the first place, and is there any way to get around this alteration?.

#include <iostream>

class CBase
{
public:
    virtual void Print() { std::cout << "CBase" << std::endl; }
};

class CDerived : public CBase
{
    int _number;

public:
    explicit CDerived(int n) : _number(n) { }
    virtual void Print() override { std::cout << "CDerived" << std::endl; }
};

CBase GetBase()
{
    return CDerived(42);
}

int main()
{
    CBase base = GetBase();
    base.Print(); // Outputs CBase
    return 0;
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
c z
  • 7,726
  • 3
  • 46
  • 59
  • 1
    Besides "that's just how C++ work", in the general case that would be dangerous -- the called virtual function might access the members (`_number` in this case) that doesn't exist. – user202729 Aug 06 '20 at 09:41
  • 1
    Part of what associates an object with its class, is the virtual table it uses. The compiler upholds this, and we have no way to change that. You aren't returning a `CDerived`, you return a `CBase`, converted from something. – StoryTeller - Unslander Monica Aug 06 '20 at 09:44
  • 1
    How could it work any other way? If you must slice off derived members, so too must you slice off derived virtual functions that might use them... Otherwise the language would encourage creation of undefined Frankenobjects, which obviously it doesn't do. (Seems the vtable doesn't get sliced or replaced; it's just that the compiler knows the returned value is a base, so it uses the base vtable when needed.) – underscore_d Aug 06 '20 at 09:51
  • 1
    This seems like an x/y problem. "_I would like to be able to return a derived class only for it's v-table,_" Why? What is the problem you're trying to solve? There _must_ be a better way. – underscore_d Aug 06 '20 at 09:54
  • @underscore_d The better way is given in the question, but what bothered me is *why* this way (creating the v-table manually) works but the C++ way does not. Especially since covariance does work when we pass a *reference*. – c z Aug 06 '20 at 10:22

1 Answers1

2

That's how C++ works.

You don't return a CDerived object, you return a CBase, copied from the CBase sub-object of the temporary CDerived.

Caleth
  • 52,200
  • 2
  • 44
  • 75