sap
sap

Reputation: 1228

template type_trait function, accept container of a given type and/or derived type

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

Answers (1)

Jonathan Wakely
Jonathan Wakely

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

Related Questions