Reputation: 836
I was looking for an alternative to the Visitor
pattern to map behaviour for types in a hierarchy of classes and maybe allow for multiple dispatch. Any time you add a new type to a hierarchy of classes, you have to update all the supporting visitors. So I came across std::type_index
, which according to cppreference.com might do the trick. I then thought, now I have to implement a get_type_index
for every subclass, which would be related to the accept
method of the visitor pattern. This would then only be half of the work that
has to be done for the Visitor
pattern. But then I saw that apparently you only have to implement a (non pure) virtual function in the base class to make the whole thing work. Below is my code and I have the following question(s):
BaseVirtual
?#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <typeindex>
struct Base
{
std::type_index get_type_index( ) const
{
return std::type_index( typeid(*this) );
}
};
struct BaseVirtual
{
virtual std::type_index get_type_index( ) const
{
return std::type_index( typeid(*this) );
}
};
template<typename BaseType>
struct A : public BaseType
{
};
template<typename BaseType>
struct B : public BaseType
{
};
template<typename BaseType>
struct C : public BaseType
{
};
int main( int argc,
char **argv )
{
std::map<std::type_index, std::string> typeToStringMap = { { std::type_index( typeid(Base) ), "Base" }, { std::type_index( typeid(A<Base> ) ), "ABase" }, { std::type_index( typeid(B<Base> ) ), "BBase" }, { std::type_index( typeid(C<Base> ) ), "CBase" }, { std::type_index( typeid(BaseVirtual) ),
"BaseVirtual" }, { std::type_index( typeid(A<BaseVirtual> ) ), "ABaseVirtual" }, { std::type_index( typeid(B<BaseVirtual> ) ), "BBaseVirtual" }, { std::type_index( typeid(C<BaseVirtual> ) ), "CBaseVirtual" } };
A<Base> a;
B<Base> b;
C<Base> c;
A<BaseVirtual> av;
B<BaseVirtual> bv;
C<BaseVirtual> cv;
auto asp = std::make_shared<A<Base>>( );
auto bsp = std::make_shared<B<Base>>( );
auto csp = std::make_shared<C<Base>>( );
auto avsp = std::make_shared<A<BaseVirtual>>( );
auto bvsp = std::make_shared<B<BaseVirtual>>( );
auto cvsp = std::make_shared<C<BaseVirtual>>( );
A<Base>* ap = new A<Base>;
B<Base>* bp = new B<Base>;
C<Base>* cp = new C<Base>;
A<BaseVirtual>* avp = new A<BaseVirtual>;
B<BaseVirtual>* bvp = new B<BaseVirtual>;
C<BaseVirtual>* cvp = new C<BaseVirtual>;
std::cout << typeToStringMap[a.get_type_index( )] << std::endl;
std::cout << typeToStringMap[b.get_type_index( )] << std::endl;
std::cout << typeToStringMap[c.get_type_index( )] << std::endl;
std::cout << typeToStringMap[av.get_type_index( )] << std::endl;
std::cout << typeToStringMap[bv.get_type_index( )] << std::endl;
std::cout << typeToStringMap[cv.get_type_index( )] << std::endl;
std::cout << typeToStringMap[asp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[bsp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[csp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[avsp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[bvsp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[cvsp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[ap->get_type_index( )] << std::endl;
std::cout << typeToStringMap[bp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[cp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[avp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[bvp->get_type_index( )] << std::endl;
std::cout << typeToStringMap[cvp->get_type_index( )] << std::endl;
}
Output:
Base
Base
Base
ABaseVirtual
BBaseVirtual
CBaseVirtual
Base
Base
Base
ABaseVirtual
BBaseVirtual
CBaseVirtual
Base
Base
Base
ABaseVirtual
BBaseVirtual
CBaseVirtual
Upvotes: 0
Views: 1013
Reputation: 52611
Why are types mapped correctly for classes inheriting from
BaseVirtual
Because the C++ standard says they should:
[expr.typeid]/2 When
typeid
is applied to a glvalue expression whose type is a polymorphic class type (10.3), the result refers to astd::type_info
object representing the type of the most derived object (1.8) (that is, the dynamic type) to which the glvalue refers.
How does the compiler do it and how expensive is it?
Irrelevant implementation details. One way is to add a pointer to type_info
, or something substantially similar, to the vtable.
Is it portable?
Yes it is. Again, the behavior you observe is mandated by the C++ standard.
Upvotes: 1