My program crashed by SIGSEGV when I try to dynamic_cast a Base class "this" pointer to a Derived class pointer. I am wondering if it has anything to do with std::bind and std::ref. Below is my example code which can produce the problem.
// header h.hpp
#include <functional>
#include <iostream>
#include <map>
#include <memory>
#include <vector>
using namespace std;
template <typename T>
class Derived;
class Base {
public:
virtual ~Base() {}
template <typename T>
void foo(const T &t) {
cout << "Base foo\n";
auto derived = dynamic_cast<Derived<T> *>(this); // SIGSEGV crash!
derived->foo(t);
}
private:
};
template <typename T>
class Derived : public Base {
public:
void foo(const T &t) { cout << "Derived foo\n"; }
private:
};
class Outter {
public:
void init();
void run();
private:
void run_foo(shared_ptr<Base> &base);
class Inner {
public:
void run() {
for (const auto &f : foos_) {
f();
}
}
void set_foo(function<void()> f) {
foos_.push_back(f);
}
private:
static vector<function<void()>> foos_;
};
Inner inner_;
vector<shared_ptr<Base>> bases_;
};
// source main.cpp
#include "h.hpp"
vector<function<void()>> Outter::Inner::foos_;
void Outter::init() {
shared_ptr<Base> base = make_shared<Derived<int>>();
bases_.push_back(base);
// inner_.set_foo(bind(&Outter::run_foo, this, base)); // no ref version
inner_.set_foo(bind(&Outter::run_foo, this, ref(base))); // ref verison
}
void Outter::run() { inner_.run(); }
void Outter::run_foo(shared_ptr<Base> &base) {
int t = 123;
base->foo(t);
}
int main() {
Outter outter;
outter.init();
outter.run();
return 0;
}
If I commented out the ref version and use the "no-ref" version, the program runs fine and won't break. It seems like the std::ref function is chopping the polymorphism off of the Base shared_ptr, why is that so?
Nothing to do with shared_ptr
or polymorphism, your shared_ptr<Base> base
simply goes out of scope.
Consider this example:
void foo(std::shared_ptr<int>& ptr)
{
std::cout << *ptr << std::endl;
}
int main()
{
std::function<void()> f;
std::shared_ptr<int> backup;
{
std::shared_ptr<int> ptr{new int{7}};
backup = ptr;
f = std::bind(foo, std::ref(ptr));
// f = std::bind(foo, ptr);
}
f();
return 0;
}
If you use std::ref
, calling f();
will have undefined behaviour, because ptr
is out of scope and foo
has a dangling reference, regardless of the fact that the integer it managed is still shared with another shared_ptr still in the scope.