To understand the problems with object slicing, I thought I have created a horrible example and I was trying to test it. However, the example is not as bad as I thought it would be.
Below is a minimal working example, and I would appreciate if you helped me understand why it is still "working properly". It would be even better if you helped me make the example worse.
#include <functional>
#include <iostream>
template <class T> class Base {
protected:
std::function<T()> f; // inherited
public:
Base() : f{[]() { return T{0}; }} {} // initialized
virtual T func1() const { return f(); }
virtual ~Base() = default; // avoid memory leak for children
};
template <class T> class Child : public Base<T> {
private:
T val;
public:
Child() : Child(T{0}) {}
Child(const T &val) : Base<T>{}, val{val} { // initialize Base<T>::f
Base<T>::f = [&]() { return this->val; }; // copy assign Base<T>::f
}
T func1() const override { return T{2} * Base<T>::f(); }
void setval(const T &val) { this->val = val; }
};
template <class T> T indirect(const Base<T> b) { return b.func1(); }
int main(int argc, char *argv[]) {
Base<double> b;
Child<double> c{5};
std::cout << "c.func1() (before): " << c.func1() << '\n'; // as expected
c.setval(10);
std::cout << "c.func1() (after): " << c.func1() << '\n'; // as expected
std::cout << "indirect(b): " << indirect(b) << '\n'; // as expected
std::cout << "indirect(c): " << indirect(c) << '\n'; // not as expected
return 0;
}
The output I get when I compile the code is as follows:
c.func1() (before): 10
c.func1() (after): 20
indirect(b): 0
indirect(c): 10
I would expect the last line to throw some exception or simply fail. When the base part of c
gets sliced in indirect
, there is no this->val
to be used inside the lambda expression (I know, C++ is a statically compiled language, not a dynamic one). I have also tried capturing this->val
by value when copy assigning Base<T>::f
, but it did not change the result.
Basically, my question is two folds. First, is this undefined behaviour, or simply a legal code? Second, if this is a legal code, why is the behaviour not affected by slicing? I mean, I can see that T func1() const
is called from the Base<T>
part, but why is the captured value not causing any trouble?
Finally, how can I build an example to have worse side-effects such as memory access type of problems?
Thank you in advance for your time.
EDIT. I am aware of the other topic that has been marked as duplicate. I have read all the posts there, and in fact, I have been trying to duplicate the last post there. As I have asked above, I am trying to get the behaviour
Then the information in b about member bar is lost in a.
which I cannot get fully. To me, only partial information seems to be lost. Basically, in the last post, the person claims
The extra information from the instance has been lost, and f is now prone to undefined behaviour.
In my example, f
seems to be working just as well. Instead, I just have the call to T Base<T>::func1() const
, which is no surprise.