Getting type of Class inside a Class Method

I want to determine the type of this variable inside a class method.

For example, take the following example –

#include <iostream>
#include <cstdlib>
#include <map>

class Base{

};


using getPrintString = std::map<Base, std::string>;

class A : public Base{
    public:
    void print(getPrintString& stringMap){
        std::string toPrint = stringMap[std::remove_reference<decltype(*this)>::type];
        std::cout<<toPrint<<std::endl;
    }
};

class B: public Base{
    public:
    void print(getPrintString& stringMap){
        std::string toPrint = stringMap.at(std::remove_reference<decltype(*this)>::type);
        std::cout<<toPrint<<std::endl;
    }
};

getPrintString stringMap{
    {std::declval<A>(), "A String"},
    {std::declval<B>(), "B String"}
};


int main()
{
    A a;
    B b;
    a.print(stringMap);
    b.print(stringMap);
}

In the above code, I want to decide what to print, depending on which class is calling the print method. I thought the class type of the method can be ascertained using decltype(*this). as for any Class type X, this will be a pointer of type X*, followed by std::remove_reference, as I am using decltype on a variable.

However, upon compilation, I run into the following error –

test.cpp: In member function ‘void A::print(getPrintString&)’:
test.cpp:15:85: error: expected primary-expression before ‘]’ token
   15 |         std::string toPrint = stringMap[std::remove_reference<decltype(*this)>::type];
      |                                                                                     ^
test.cpp: In member function ‘void B::print(getPrintString&)’:
test.cpp:23:88: error: expected primary-expression before ‘)’ token
   23 |         std::string toPrint = stringMap.at(std::remove_reference<decltype(*this)>::type);
      |                                                                                        ^
In file included from /usr/include/c++/11/bits/move.h:57,
                 from /usr/include/c++/11/bits/exception_ptr.h:43,
                 from /usr/include/c++/11/exception:153,
                 from /usr/include/c++/11/ios:39,
                 from /usr/include/c++/11/ostream:38,
                 from /usr/include/c++/11/iostream:39,
                 from test.cpp:1:
/usr/include/c++/11/type_traits: In instantiation of ‘decltype (__declval<_Tp>(0)) std::declval() [with _Tp = A; decltype (__declval<_Tp>(0)) = A&&]’:
test.cpp:29:21:   required from here
/usr/include/c++/11/type_traits:2366:47: error: static assertion failed: declval() must not be used!
 2366 |       static_assert(__declval_protector<_Tp>::__stop,
      |                                               ^~~~~~
/usr/include/c++/11/type_traits:2366:47: note: ‘std::__declval_protector<A>::__stop’ evaluates to false
/usr/include/c++/11/type_traits: In instantiation of ‘decltype (__declval<_Tp>(0)) std::declval() [with _Tp = B; decltype (__declval<_Tp>(0)) = B&&]’:
test.cpp:30:21:   required from here
/usr/include/c++/11/type_traits:2366:47: error: static assertion failed: declval() must not be used!
/usr/include/c++/11/type_traits:2366:47: note: ‘std::__declval_protector<B>::__stop’ evaluates to false
In file included from /usr/include/c++/11/string:48,
                 from /usr/include/c++/11/bits/locale_classes.h:40,
                 from /usr/include/c++/11/bits/ios_base.h:41,
                 from /usr/include/c++/11/ios:42,
                 from /usr/include/c++/11/ostream:38,
                 from /usr/include/c++/11/iostream:39,
                 from test.cpp:1:
/usr/include/c++/11/bits/stl_function.h: In instantiation of ‘constexpr bool std::less<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Base]’:
/usr/include/c++/11/bits/stl_tree.h:2174:33:   required from ‘std::pair<std::_Rb_tree_node_base*, std::_Rb_tree_node_base*> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_get_insert_hint_unique_pos(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator, const key_type&) [with _Key = Base; _Val = std::pair<const Base, std::__cxx11::basic_string<char> >; _KeyOfValue = std::_Select1st<std::pair<const Base, std::__cxx11::basic_string<char> > >; _Compare = std::less<Base>; _Alloc = std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator = std::_Rb_tree<Base, std::pair<const Base, std::__cxx11::basic_string<char> >, std::_Select1st<std::pair<const Base, std::__cxx11::basic_string<char> > >, std::less<Base>, std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > > >::const_iterator; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::key_type = Base]’
/usr/include/c++/11/bits/stl_tree.h:2234:4:   required from ‘std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique_(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator, _Arg&&, _NodeGen&) [with _Arg = const std::pair<const Base, std::__cxx11::basic_string<char> >&; _NodeGen = std::_Rb_tree<Base, std::pair<const Base, std::__cxx11::basic_string<char> >, std::_Select1st<std::pair<const Base, std::__cxx11::basic_string<char> > >, std::less<Base>, std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > > >::_Alloc_node; _Key = Base; _Val = std::pair<const Base, std::__cxx11::basic_string<char> >; _KeyOfValue = std::_Select1st<std::pair<const Base, std::__cxx11::basic_string<char> > >; _Compare = std::less<Base>; _Alloc = std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree<Base, std::pair<const Base, std::__cxx11::basic_string<char> >, std::_Select1st<std::pair<const Base, std::__cxx11::basic_string<char> > >, std::less<Base>, std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > > >::iterator; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::const_iterator = std::_Rb_tree<Base, std::pair<const Base, std::__cxx11::basic_string<char> >, std::_Select1st<std::pair<const Base, std::__cxx11::basic_string<char> > >, std::less<Base>, std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > > >::const_iterator]’
/usr/include/c++/11/bits/stl_tree.h:1102:23:   required from ‘std::__enable_if_t<std::is_same<_Val, typename std::iterator_traits<_InputIterator>::value_type>::value> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_range_unique(_InputIterator, _InputIterator) [with _InputIterator = const std::pair<const Base, std::__cxx11::basic_string<char> >*; _Key = Base; _Val = std::pair<const Base, std::__cxx11::basic_string<char> >; _KeyOfValue = std::_Select1st<std::pair<const Base, std::__cxx11::basic_string<char> > >; _Compare = std::less<Base>; _Alloc = std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > >; std::__enable_if_t<std::is_same<_Val, typename std::iterator_traits<_InputIterator>::value_type>::value> = void; typename std::iterator_traits<_InputIterator>::value_type = std::pair<const Base, std::__cxx11::basic_string<char> >]’
/usr/include/c++/11/bits/stl_map.h:232:36:   required from ‘std::map<_Key, _Tp, _Compare, _Alloc>::map(std::initializer_list<std::pair<const _Key, _Tp> >, const _Compare&, const allocator_type&) [with _Key = Base; _Tp = std::__cxx11::basic_string<char>; _Compare = std::less<Base>; _Alloc = std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > >; std::map<_Key, _Tp, _Compare, _Alloc>::allocator_type = std::allocator<std::pair<const Base, std::__cxx11::basic_string<char> > >]’
test.cpp:31:1:   required from here
/usr/include/c++/11/bits/stl_function.h:400:20: error: no match for ‘operator<’ (operand types are ‘const Base’ and ‘const Base’)
  400 |       { return __x < __y; }

Filtering out, there are two main error –
first

test.cpp: In member function ‘void A::print(getPrintString&)’:
test.cpp:15:85: error: expected primary-expression before ‘]’ token
   15 |         std::string toPrint = stringMap[std::remove_reference<decltype(*this)>::type];
      |                                                                                     ^
test.cpp: In member function ‘void B::print(getPrintString&)’:
test.cpp:23:88: error: expected primary-expression before ‘)’ token
   23 |         std::string toPrint = stringMap.at(std::remove_reference<decltype(*this)>::type);
      |         

Which comes from the use of decltype. How to use decltype correctly in this context.
Second, there is an another internal error using declval, what am I doing wrong and how to fix it ?

TIA

>Solution :

If you want to build a map of types, you need to use typeid to get the type_info and then use its type_index (or hash value) as Key in the map.

Example:

#include <cstdlib>
#include <iostream>
#include <map>

#include <typeindex>
#include <typeinfo>

class Base {
public:
    virtual ~Base() = default;
};

using getPrintString = std::map<std::type_index, std::string>;

class A : public Base {
public:
    void print(getPrintString& stringMap) {
        std::string toPrint = stringMap[std::type_index(typeid(decltype(*this)))];
        std::cout << toPrint << std::endl;
    }
};

class B : public Base {
   public:
    void print(getPrintString& stringMap) {
        std::string toPrint = stringMap[std::type_index(typeid(decltype(*this)))];
        std::cout << toPrint << std::endl;
    }
};

int main() {
    getPrintString stringMap{{std::type_index(typeid(A)), "A String"},
                             {std::type_index(typeid(B)), "B String"}};

    A a;
    B b;
    a.print(stringMap);
    b.print(stringMap);
}

Leave a Reply