Type of facility changing during construction

I just discovered the following behavior: having an object of type B derived from type A , the final type at construction time A is A , not B This can be seen in 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; } 

Running this code (compiled with gcc 4.8.5) is as follows:

 0x400ae0 0x400ae0 0x400ac0 

We see that the type returned by typeid in A::A() is A , not B , and then the final type changes to B

Why?

Is it possible to find out the "real" final type when building the parent class?

My context is as follows:

I have a parent Resource class and several classes that inherit it. I also have a ResourceManager notified by each resource creation and need to know the final type of resource created. What I am doing to avoid code duplication is the following, but it does not 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 that I can execute a notification in each constructor / destructor of children, but it is less reliable (possible error if the resource is instantiated without notifying the manager). Do you have an idea for a workaround?

+5
source share
2 answers

It looks like you are looking for 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 complete when a notifyCreation call is notifyCreation . You can specify the address of the MyResource instance, but that’s all you can do with the instance. (Thanks to Kaleth for pointing this out)

In particular, from [class.cdtor]

If the typeid operand refers to an object under construction or destruction, and the static type of the operand is neither a constructor class nor a destructor, nor one of its foundations, the behavior is undefined.

Therefore, the ResourceManager must be implemented somewhat in order to enable the use of 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 } }; 
+5
source

There is no good way in the constructor to do this in the constructor, 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 this outside the constructor, you can also provide a virtual function in "A" and overwrite it in "B".

+1
source

Source: https://habr.com/ru/post/1269837/


All Articles