2016-02-14 2 views
1

У меня есть несколько шаблонов проверочных типа: is_object от type_traits и isSupportedContainer реализована следующим образом:Рекурсивная проверка типа с использованием шаблонов

template <class T> 
struct isSupportedContainer 
    : public false_type {}; 

template <class T> 
struct isSupportedContainer < list<T> > 
    : public true_type {}; 

/*other containers*/ 

Я хочу сделать рекурсивную проверку isSupported применения себя не только к тип контейнера, но также и к содержащемуся типу. Текущая реализация:

// using std::__and_ and std::for from type_traits 
template <class T> 
struct isSupported 
    : public __and_ < __or_ < is_object<T>, isSupportedContainer<T> >, 
         isSupported <T> > {}; 

Когда я называю isSupported < vector<int> >::value, он генерирует кучу ошибок компиляции (укороченный):

In instantiation of 'struct std::__and_<...>, isSupportedContainer<std::vector<...> > >, isSupported<std::vector<...> > >': 
required from 'struct isSupported<std::vector<int> >' 
required from /*its call in main()*/ 
error: invalid use of incomplete type 'std::conditional<true, isSupported<std::vector<int> >, std::__or_<...>, isSupportedContainer<std::vector<...> > > >::type' 
struct __and_<_B1, _B2> 
     ^
In /*file containing isSupported and isSupportedContainer*/: 
error: declaration of 'std::conditional<true, isSupported<std::vector<int> >, std::__or_<...> >::type' 
struct isSupported 
     ^
In function 'int main()': 
error: 'value' is not a member of 'isSupported<std::vector<int> >' 
cout << isSupported < vector<int> >::value; 
     ^

Итак, как такая проверка будет осуществляться?

Примеры: предполагающие list и vector в качестве поддерживаемых классов vector<list<int>> также поддерживается и vector<list<vector<string>>> не

UPD: рабочая версия
UPD2: нет, не работает

template <class T> 
struct isSupported 
    : public isSupportedSimpleObject<T> {}; //is_object turned out to be a wrong thing 

template <template<class> class T, class U> 
struct isSupported < T<U> > 
    : public __and_ < isSupportedContainer<T<U>>, 
         isSupported <U> > {}; 
+1

Вы, кажется, используете 'isSupported ' в определении 'isSupported'. Кроме того, 'std :: is_object', вероятно, не будет делать то, что вы ожидаете от него. –

+0

О, да, это ошибка. О 'is_object': почему? Я ожидаю, что это позволит непустым фундаментальным типам, массивам и указателям. Перечисления и союзы также приемлемы. – Alex

+1

'is_object' позволяет * все * через, включая неподдерживаемые контейнеры. Он только отфильтровывает функции, ссылки и 'void'.Если вы хотите поддерживать все типы неконтейнеров и некоторые контейнеры, вам будет трудно различать контейнеры из неконтейнеров. На уровне языка нет различия. –

ответ

2

Вы могли бы упростить весь расчет, если вы используете enable_if в начальной замене.

#include <type_traits> 
#include <list> 
#include <vector> 
#include <string> 

template <class T> 
struct isSupportedContainer : std::false_type {}; 

template <class T> 
struct isSupportedContainer < std::list<T> > : std::true_type {}; 

template <class T> 
struct isSupportedContainer < std::vector<T> > : std::true_type {}; 

template <class T, typename = void> // #1 
struct isSupported : std::integral_constant<bool, std::is_object<T>::value> {}; 

template <class Cont> // #2 
struct isSupported<Cont, typename std::enable_if<isSupportedContainer<Cont>::value>::type> 
    : isSupported<typename Cont::value_type> {}; 


int main() { 
    static_assert(isSupported<std::vector<int>>::value,""); 
    static_assert(isSupported<std::vector<std::list<int>>>::value,""); 
    static_assert(isSupported<std::string>::value,""); 
    static_assert(!isSupported<int&>::value,""); 
    return 0; 
} 

Live Demo

Этот метод (если это действительно то, что вы после этого) основана на принципе, для которого std::void_t был добавлен в C++ 17

ключевые моменты заключаются в следующем:

  1. Основное определение шаблона принимает два параметра типа. первый из них называется T, а другой - неназванным и имеет тип по умолчанию void. Эта версия просто проверяет ваш базовый регистр.
  2. При создании экземпляра шаблона компилятор сопоставляет основной экземпляр isSupported<YourType, void>.
    Теперь компилятор проверяет, есть ли какая-либо специализация, которая соответствует типам аргументов <YourType, void>, поэтому она рассматривает предоставленную специализацию. std::enable_if используется для определения этого типа на самом деле поддерживаемый контейнер, и если он возвращает void, и мы сопоставляем типы, выведенные изначально <YourType, void>. Поэтому компилятор рассматривает специализацию как лучшее совпадение, и мы делаем рекурсивную проверку.
  3. Если, однако, поставляемый тип не поддерживается, std::enable_if не имеет члена type, а SFINAE выполняет вход.
+0

Кажется, это эквивалентно тому, что я добавил в UPD, но я не могу понять и не найти точного значения 'typename = void' и всей структуры ._. Не могли бы вы объяснить это? – Alex

+0

@Alex, см. Мое редактирование. Дайте мне знать, если что-то нужно прояснить. – StoryTeller

+1

@StoryTeller После второй мысли. Я думаю, что ваше решение прекрасно решает вопрос :-) – Lingxi