public
Created

Full SFINAE type trait for containers

  • Download Gist
gistfile1.c++
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
#include <vector>
#include <string>
#include <utility>
#include <iostream>
 
template<typename T>
struct has_const_iterator
{
private:
typedef char one;
typedef struct { char array[2]; } two;
 
template<typename C> static one test(typename C::const_iterator*);
template<typename C> static two test(...);
public:
static const bool value = sizeof(test<T>(0)) == sizeof(one);
typedef T type;
};
 
template <typename T>
struct has_begin_end
{
struct Dummy { typedef void const_iterator; };
typedef typename std::conditional<has_const_iterator<T>::value, T, Dummy>::type TType;
typedef typename TType::const_iterator iter;
 
struct Fallback { iter begin() const; iter end() const; };
struct Derived : TType, Fallback { };
 
template<typename C, C> struct ChT;
 
template<typename C> static char (&f(ChT<iter (Fallback::*)() const, &C::begin>*))[1];
template<typename C> static char (&f(...))[2];
template<typename C> static char (&g(ChT<iter (Fallback::*)() const, &C::end>*))[1];
template<typename C> static char (&g(...))[2];
 
static bool const beg_value = sizeof(f<Derived>(0)) == 2;
static bool const end_value = sizeof(g<Derived>(0)) == 2;
};
 
template <typename T>
struct is_container
{
static const bool value = has_const_iterator<T>::value &&
has_begin_end<T>::beg_value && has_begin_end<T>::end_value;
};
 
struct Foo { typedef int const_iterator; };
struct Goo { typedef int const_iterator; const_iterator begin() const; };
typedef std::pair<int, int> PT;
 
int main()
{
std::cout << is_container<int>::value << std::endl
<< is_container<Foo>::value << std::endl
<< is_container<Goo>::value << std::endl
<< is_container<PT>::value << std::endl
<< is_container<std::vector<int>>::value << std::endl
<< is_container<std::string>::value << std::endl
;
}

To be extremely pedantic on Line 24, I could have said:

typedef typename std::conditional<std::is_class<T>::value && has_const_iterator<T>::value, T, Dummy>::type TType;

However, I assume that anything that passes has_const_iterator must already be of class type, so I believe that's superfluous.

Also, why isn't static bool evaluation short-circuited?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.