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