C++ define a class member type according to its template

狂风中的少年 提交于 2019-12-14 04:18:28

问题


In our code we have the following class:

template<class A, class B>
class myClass
{
    typedef std::list<Object<A,B>> MyList;
    typedef std::map<A, typename mList::iterator> MyMap

    MyList mList;
    MyMap mMap;
}

class A is metaprogrammed and it can be a string, int and so on. I would like to change the code so in case class A is a "meta string" a map will be used, otherwise unordered_map will be used.

I've tried to add some more meta programming but haven't succeeded yet:

template< class A, class B>
struct MapType // default
{
    typedef std::list<Object<A,B>> MyList;
    typedef std::unordered_map<A,B> MyMap;
}

//TODO struct with templated A (string) to use std::map

template<class A, class B>
class myClass
{
    ???? // if A ~ String define myMap to MAP . otherwise unordered

    MyList mList;
    MyMap mMap;
}

any other suggestions for using different map type will be appreciated as well.

Thanks


回答1:


A simple solution would be to use std::conditional to check if A is the same as your "meta string" class (I picked std::string for demonstration purposes):

template<class A, class B>
class myClass
{
    std::list<Object<A,B>> mList;
    std::conditional_t<std::is_same<A,std::string>::value,
                       std::map<A,B>, std::unordered_map<A,B>> mMap;
};

Another possibility would be to use partial specialization:

template<class A, class B>
class myClass
{
    std::list<Object<A,B>> mList;
    std::unordered_map<A,B> mMap;
};

template<class B>
class myClass<std::string,B>
{
    std::list<Object<std::string,B>> mList;
    std::map<std::string,B> mMap;
};



回答2:


Use std::conditonal trait:

template<class A, class B>
class myClass
{
    using MyList = std::list<Object<A,B>>;
    using MyMap = std::conditional_t<IsMetaString<A>::value,
                        std::map<A, typename mList::iterator>,
                        std::unordered_map<A, typename mList::iterator>>;

    MyList mList;
    MyMap mMap;
}

Please note I took the liberty to replace your tyedefs with the using type alias, which you should do too.

What is left is to implement IsMetaString, which depending on your definition of Meta String could be as simple as:

template <class T> struct IsMetaString : std::false_type {};
template <> struct IsMetaString<std::string> : std::true_type {};

for instance if by meta string you mean std::string. Or you could modify it to your needs.

Also I think you meant typename MyList::iterator instead of typename mList::iterator.




回答3:


Assuming a "meta string" has type foo (a fictitious name I just made up, since you haven't specified what a "meta string" is, you can simply do

template< class A, class B>
struct MapType // default
{
    typedef std::list<Object<A,B>> MyList;
    typedef std::unordered_map<A,B> MyMap;

    MyList mList;
    MyMap mMap;
};


template<class B>
struct MapType<foo, B>
{
     typedef std::list<Object<foo, B>> MyList;
     typedef std::map<foo,B> MyMap;

     MyList mList;
     MyMap mMap;
};



回答4:


With partial specialization (to be used in pre-C++11 which might not have std::conditional), you do not need to specialize the entire class (which might be a big burden of copying a lot of methods), but you can specialize just the map type:

template<class A, class B>
class myClass
{
    template<class X, class ITER_T>
    struct map_type {
        typedef std::map<X, ITER_T> type;
    };

    template<class ITER_T>
    struct map_type<std::string, ITER_T> {
        typedef boost::unordered_map<A /* or std::string */, ITER_T> type;
    };

    typedef std::list<Object<A,B>> MyList;
    typedef map_type<A, typename MyList::iterator>::type MyMap;

    MyList mList;
    MyMap mMap;
};

(for pre-C++11 I'd use boost::unordered_map instead of std::unordered_map)



来源:https://stackoverflow.com/questions/40356688/c-define-a-class-member-type-according-to-its-template

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!