Reputation: 1228
in my work we do a lot of pair programming, and i wrote a function that ONLY accepts containers of a SINGLE type or its derivates, but my co-worker is afraid it will fail code review because it looks so damn ugly and says there's gotta be a better way:
here is the signature, the Fruit class is a base class that i renamed Fruit just for this thread:
template <class Container>
typename enable_if<is_base_of<Fruit, typename remove_pointer<typename Container::value_type>::type>::value, void>::type
processFruits(container& fruits)
{
//process the elements and change them if needed. thats why no const
}
what it does is: returns void and enables the function IF its a container AND the type inside the container is a Fruit and/or derivided of fruit. i also used std::remove_pointer because i needed to know the "type" of the pointer (the container will most likely have pointers).
this compiles and works as intended, but as i said i don't know it its the best way to do it, it seems too verbose and might get cut of on code review.
EDIT: this also accepts templated classes, don't have to be containers. is there a way i can limit it to only accept STL containers?
any alternate ideas or is it fine like it is? thanks in advance.
Upvotes: 1
Views: 452
Reputation: 171263
It is a bit horrible to read.
Well for starters you don't need to say enable_if<B, void>
you can just say enable_if<B>
and use the default template argument.
You can easily split it into separate pieces:
template <class T>
struct is_fruity
: is_base_of<Fruit, T>
{ };
template <class Container, typename Value = typename Container::value_type>
struct is_fruit_container
: is_fruity<typename remove_pointer<Value>::type>>
{ };
template<class Container>
typename enable_if<is_fruit_container<Container>::value>::type
processFruits(Container& fruits)
{
//process the elements and change them if needed. thats why no const
}
If you have a compiler supporting alias templates you can make it even easier to read:
template<typename Cond>
using Require = typename enable_if<Cond::value>::type;
template<class Container>
Require<is_fruit_container<Container>>
processFruits(Container& fruits)
{
//process the elements and change them if needed. thats why no const
}
this also accepts templated classes, don't have to be containers. is there a way i can limit it to only accept STL containers?
I'm not sure what you mean by "templated classes", it only accepts types with a nested value_type
type which is a type derived from Fruit
or a pointer to such type, it doesn't have to be a template. To limit it to "STL containers" you need to write a trait to indentify an "STL container", however you want to define that. To do it properly you'd need a trait that tests for begin()
, end()
, size()
members, as well as all the nested types specified by the container requirements, iterator
, value_type
, etc.
template<typename C, typename Chead, typename... Ctail>
struct static_and
{
static const bool value = C::value && static_and<Chead, Ctail...>::value;
};
template<typename C>
struct static_and<C>
{
static const bool value = C::value;
};
template<typename C>
struct is_container
: static_and<has_begin<C>, has_end<C>, has_iterator<C>, has_value_type<C> /* etc */>
{ };
Upvotes: 1