Is it possible to write a type trait whose value is true for all common STL structures (e.g., vector
, set
, map
, ...)?
To get s
Why not do something like this for is_container?
template <typename Container>
struct is_container : std::false_type { };
template <typename... Ts> struct is_container<std::list<Ts...> > : std::true_type { };
template <typename... Ts> struct is_container<std::vector<Ts...> > : std::true_type { };
// ...
That way users can add their own containers by partially-specializing. As for is_vector et-al, just use partial specialization as I did above, but limit it to only one container type, not many.
Fast forward to 2018 and C++17, I was so daring to improve on @Frank answer
// clang++ prog.cc -Wall -Wextra -std=c++17
#include <iostream>
#include <vector>
namespace dbj {
template<class T>
struct is_vector {
using type = T ;
constexpr static bool value = false;
};
template<class T>
struct is_vector<std::vector<T>> {
using type = std::vector<T> ;
constexpr static bool value = true;
};
// and the two "olbigatory" aliases
template< typename T>
inline constexpr bool is_vector_v = is_vector<T>::value ;
template< typename T>
using is_vector_t = typename is_vector<T>::type ;
} // dbj
int main()
{
using namespace dbj;
std::cout << std::boolalpha;
std::cout << is_vector_v<std::vector<int>> << std::endl ;
std::cout << is_vector_v<int> << std::endl ;
} /* Created 2018 by dbj@dbj.org */
The "proof the pudding". There are better ways to do this, but this works for std::vector
.
The way I like to detect whether something is a container is to look for data()
and size()
member functions. Like this:
template <typename T, typename = void>
struct is_container : std::false_type {};
template <typename T>
struct is_container<T
, std::void_t<decltype(std::declval<T>().data())
, decltype(std::declval<T>().size())>> : std::true_type {};
Actually, after some trial and error I found it's quite simple:
template<class T>
struct is_vector<std::vector<T> > {
static bool const value = true;
};
I'd still like to know how to write a more general is_container
. Do I have to list all types by hand?
template <typename T>
struct is_container {
template <
typename U,
typename I = typename U::const_iterator
>
static int8_t test(U* u);
template <typename U>
static int16_t test(...);
enum { value = sizeof test <typename std::remove_cv<T>::type> (0) == 1 };
};
template<typename T, size_t N>
struct is_container <std::array<T,N>> : std::true_type { };
Look, another SFINAE-based solution for detecting STL-like containers:
template<typename T, typename _ = void>
struct is_container : std::false_type {};
template<typename... Ts>
struct is_container_helper {};
template<typename T>
struct is_container<
T,
std::conditional_t<
false,
is_container_helper<
typename T::value_type,
typename T::size_type,
typename T::allocator_type,
typename T::iterator,
typename T::const_iterator,
decltype(std::declval<T>().size()),
decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end()),
decltype(std::declval<T>().cbegin()),
decltype(std::declval<T>().cend())
>,
void
>
> : public std::true_type {};
Of course, you might change methods and types to be checked.
If you want to detect only STL containers (it means std::vector
, std::list
, etc) you should do something like this.