I just discoverd the following behaviour : having an object of type B
derived from type A
, the final type during the construction of A
is A
and not B
. This can be observed with the following example :
#include <iostream>
#include <typeinfo>
class A
{
public:
A() { std::cout << &typeid(*this) << std::endl; }
};
class B : public A
{
public:
B() : A() { std::cout << &typeid(*this) << std::endl; }
};
int main()
{
A a;
B b;
return 0;
}
A run of this code (compiled with gcc 4.8.5) is the following :
0x400ae0
0x400ae0
0x400ac0
We can see that the type returned by typeid in A::A()
is A
and not B
, and then the final type changes to become B
.
Why ?
Is it possible to know the "real" final type during the construction of the parent class ?
My context is the following :
I have a parent class Resource
and several classes inheriting from it. I also have a ResourceManager
notified by each creation of a resource, and having to know the final type of the created resource. What I'm doing to avoid duplicated code is the following, but it doesn't work :
class Resource
{
public:
Resource() { ResourceManager::notifyCreation(*this); }
~Resource() { ResourceManager::notifyDestruction(*this); }
};
class MyResource : public Resource
{
// I don't have to care to the manager here
};
I know I can do the notification in each constructor/destructor of the children, but it's less robust (possible bug if a resource is instanciated without notification to the manager). Have you any idea for a workaround ?
Sounds like what you're looking for is CRTP
template<typename Concrete>
struct Resource
{
Resource() { ResourceManager::notifyCreation(*static_cast<Concrete*>(this)); }
~Resource() { ResourceManager::notifyDestruction(*static_cast<Concrete*>(this)); }
};
struct MyResource : Resource<MyResource>
{
};
Note that MyResource
is not yet finished constructed when the call to notifyCreation
is made. The address of the MyResource
instance can be taken, but that's about all that can be done to the instance. (Thanks to Caleth for pointing this out)
In particular from [class.cdtor]
If the operand of
typeid
refers to the object under construction or destruction and the static type of the operand is neither the constructor or destructor's class nor one of its bases, the behavior is undefined.
Therefore ResourceManager
would have to be implemented somewhat like this to enable using typeid
struct ResourceManager
{
template<typename T>
void notifyCreation(T&&)
{
add(typeid(T)); // can't apply to an expression
}
template<typename T>
void notifyDestruction(T&&)
{
remove(typeid(T)); // can't apply to an expression
}
};
There is no good way to do it in a constructor as in your example, but you can provide a special constructor for A
, i.e.
A(const std::type_info &info) {
std::cout << info.name() << std::endl;
}
and in B
B() : A(typeid(*this)) {
std::cout << typeid(*this).name() std::endl;
}
if you do it outside the constructor, you can also provide a virtual function in 'A' and overwrite it in 'B'.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With