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.
There is no undefined behavior with your current code. However, it's dangerous and therefore easy to make undefined behavior with it.
The slicing happen, and yet you access this->val
. Seems like magic, but you're just accessing the this->val
from Child<double> c
from your main!
That's because of the lambda capture. You capture this
, which points to your c
variable in your main. You then assign that lambda into a std::function
inside your base class. You base class now have a pointer to the c
variable, and a way to access the val
through the std::function
.
So the slicing occurs, but you access to the unsliced object.
This is also why the number is not multiplied by two. The virtual call resolves to base, and the value of val
in c
in your main is 10
.
Your code is roughly equivalent to that:
struct B;
struct A {
B* b = nullptr;
int func1() const;
};
struct B : A {
int val;
explicit B(int v) : A{this}, val{v} {}
};
int A::func1() const {
return b->val;
}
int main() {
B b{10};
A a = b;
std::cout << a.func1() << std::endl;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.