问题
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