问题
I have an issue similar to the following:
When is static cast safe when you are using multiple inheritance?
multiple inheritance: unexpected result after cast from void * to 2nd base class
but I really don't understand why it does not work, since I'm doing what is suggested (cast back to the original type).
Also, templates are involved.
I have a generic container of templated objects which I implement as a map of std::string
IDs and void*
:
std::map<std::string, void*> mymap;
The objects look like follows:
template <class T>
class A {
virtual void f1(const T&) = 0;
};
class T1 {};
class T2 {};
class B : public A<T1> {
void f1(const T1&) override;
}
class D {
virtual void f3 () {};
}
class C : public A<T2>,
public D
{
void f1(const T2&) override;
}
In the main code, I have something like this to add my objects to the map and call the appropriate method depending on the type:
template <class T>
void addClass(A<T>& a, std::string id){
std::pair<std::string, void*> pair(id, (void*)&a);
mymap.insert(pair);
}
template<class T>
void callback(A<T>&a, std::string typeID) {
static_cast<A<T>*>(mymap[typeID])->f1();
}
The string unambiguously identifies what class is being used as a template, so that the callback can cast back to the correct type.
Everything worked just fine as long as I passed to addClass
objects like B
, i.e. with single inheritance from the pure virtual templated class A
.
As soon as I pass an object like C
, i.e. with multiple inheritance from A
and D
, the callback
where I do the casting produces a SEGFAULT, even though I have no compilation errors.
UPDATE. Apparently, the problem was in the std::shared_ptr
use rather than in the casting itself. Here is the MCVE.
classes.hpp
:
#pragma once
#include <iostream>
#include <map>
template <class T>
class A {
public:
virtual void f1(const T&) = 0;
};
class T1 {
public:
double t1 = 10.0;
};
class T2 {
public:
short int t2 = 8;
};
class B : public A<T1> {
public:
void f1(const T1&) override;
};
class D {
public:
virtual void f3();
};
class C : public A<T2>,
public D
{
public:
void f1(const T2&) override;
};
class MyContainer{
public:
std::map<std::string, void*> mymap;
template<class T>
void addClass(A<T>& t, std::string id);
template<class T>
void callback(T& t, std::string id);
};
template<class T>
void MyContainer::addClass(A<T> &a, std::string id){
std::pair<std::string, void*> pair(id, (void*)&a);
mymap.insert(pair);
}
template<class T>
void MyContainer::callback(T& t, std::string id){
static_cast<A<T>*>(mymap[id])->f1(t);
}
classes.cpp
:
#include <classes.hpp>
void B::f1(const T1& t1){
std::cout << "Hello from B using t1: " << t1.t1 << std::endl;
}
void C::f1(const T2 & t2){
std::cout << "Hello from C using t2: " << t2.t2 << std::endl;
}
void D::f3() {
std::cout << "Hello from D" << std::endl;
}
main.cpp
:
#include <iostream>
#include <classes.hpp>
#include <memory>
using namespace std;
int main()
{
std::shared_ptr<B> b(new B()); // inherits from A<T1>
std::shared_ptr<C> c(new C()); // inherits from A<T2> and D
MyContainer container;
// no need to specify the template,
// it is implict from the class being passed
container.addClass(*b, "t1");
container.addClass(*c, "t2");
T1 t1;
T2 t2;
container.callback(t1, "t1");
container.callback(t2, "t2");
}
If i replace the shared pointer with actual objects, everything is fine:
Hello from B using t1: 10
Hello from C using t2: 8
But in my original code I need the shared pointers because there are some conditionals at runtime to whether build them or not...
CMakeLists.txt
:
cmake_minimum_required(VERSION 2.8)
add_compile_options(-std=c++11)
project(casting_problem)
include_directories(include)
add_library(${PROJECT_NAME} SHARED classes.cpp)
add_executable(${PROJECT_NAME}_exe "main.cpp")
target_link_libraries(${PROJECT_NAME}_exe ${PROJECT_NAME})
回答1:
The problem with your code is that your shared pointers are null, and therefore behaviour of indirecting through them is undefined.
You can create shared objects using std::make_shared
.
Keep in mind however, that the shared objects are destroyed as soon as last shared pointer is destroyed, at which point any pointers to the objects in your map (if any) are left dangling.
来源:https://stackoverflow.com/questions/54200669/cast-void-to-classes-with-multiple-inheritance