Reputation: 3618
What are mechanisms for ensuring that template arguments are containers?
Alternatively, how can i make different specialisations for class/function depending on whether its argument is container or not?
Upvotes: 1
Views: 171
Reputation: 136485
Another way is to use boost::spirit::traits::is_container<>
Returns
mpl::true_
ifT
has the following embedded types defined:value_type
,iterator
,size_type
, andreference
. Otherwise it will returnmpl::false_
.
Works in C++03 and above.
Upvotes: 1
Reputation: 275800
Testing for a full blown container is difficult and ambiguous. Personally, I consider something a container if and only if it owns its immediate contents, but this may not line up with other people's use of the term.
Testing for iterability is neither difficult nor ambiguous, and is often what you want to test for when taking an object in a function argument. There are iterable things that aren't very container like (C++1y's or C++1z's string_view
, as an example).
In my opinion, in C++11, an instance c of a type X is iterable iff:
for( auto&& a : c ) {}
is well formed. The wording chosen for the above means that you can extend any type with begin
and end
free function overloads to make it iterable.
A decent approximation if the above will work would be to test if, in an argument dependent enabled lookup context, that std::begin
and std::end
return something that has a valid std::iterator_traits<>
.
As a quick sketch, I get something like this:
template<typename T, typename=void>
struct is_iterable : std::false_type {};
namespace aux {
using std::begin;
// note: no implementation
template<typename C>
auto adl_begin( C&& c )->decltype( begin( std::forward<C>(c) ) );
using std::end;
// note: no implementation
template<typename C>
auto adl_end( C&& c )->decltype( end( std::forward<C>(c) ) );
}
template<typename T>
struct is_iterable<T,
typename std::enable_if<
std::is_same<
typename std::iterator_traits< typename std::decay<decltype( aux::adl_begin( std::declval<T>() ) )>::type >::iterator_category,
typename std::iterator_traits< typename std::decay<decltype( aux::adl_end( std::declval<T>() ) )>::type >::iterator_category
>::value
>::type
> : std::true_type {};
Containers, meanwhile, rarely are as uniform. Their most basic "common" processes, like adding elements, differ from one to another in ways that differ in signature and semantics. erase(iterator)
and ::allocator_type
are the two most common features of container-like structures, but a std::array<T,N>
and T[N]
are arguably containers and have neither property.
In short, beyond their common property of iterability, containers differ too much for there to be a highly useful is_container<C>
traits class. Instead, you should work out what properties of the container you are looking for (ability to delete elements? Ability to insert elements? Random access?) and test for those instead.
Upvotes: 1