jeudi 14 janvier 2021

Can I have a vector of polymorphic values under a fixed sized assumption?

Consider the below code, a derived class replaces a virtual member function with a variant of the function, but does not add any new member variables. Values of Base and Derived are added to a common container, std::vector and as expected the Derived value is sliced. However by copying the representation in memory of the Derived value into the container the value is in effect only partially sliced.

#include <iostream>
#include <vector>

class Base {
public:
    Base() = default;
    Base(float arg) : a{ arg } {};

    virtual float doSomething(float b) const { return a + b; }

    float a;
};

class Derived : public Base {
public:
    Derived() = default;
    Derived(float a) : Base{ a } {};

    float doSomething(float b) const { return a - b; }
};

int main()
{
    Base b{ 1.0f };
    Derived d{ 1.0f };

    std::cout << sizeof(b) << ", " << sizeof(d) << '\n';  // 8, 8

    std::vector<Base> v{ b, d };  // d is sliced
    std::cout << v[0].doSomething(2.0f) << '\n';  // 3
    std::cout << v[1].doSomething(2.0f) << '\n';  // 3 as d was sliced

    memcpy(&v[1], &d, sizeof(d));  // Copy the representation of d over to v[1]
    std::cout << v[1].doSomething(2.0f) << '\n';  // now -1
}

The size of the values is 8 due to the pointer to the virtual function table and this is how to above polymorphism is being realised. The type of v[1] is always Base, so if Derived added a new member function it wouldn't be possible to call it. In effect v[1] is still sliced to Base but with the reimplemented member functions of Derived.

Under the assumption that Base is essentially POD but with added virtual member functions, all of which are const i.e. memory copyable, and that Derived only reimplements those member functions:

  1. Does the above code fall into undefined behaviour?
  2. If so is there a way to implement this without the memcpy or equivalent in a way that would be defined behaviour?
  3. If this is a common pattern, what is it called?

Aucun commentaire:

Enregistrer un commentaire