Шаблонная специализация для использования типа по умолчанию, если членское определение типа класса не существует

Я пытаюсь написать код, который использует членское определение типа аргумента шаблона, но хотят предоставить тип по умолчанию, если аргумент шаблона не имеет того определения типа. Упрощенный пример, который я попробовал, является этим:

struct DefaultType    { DefaultType()    { printf("Default ");    } };
struct NonDefaultType { NonDefaultType() { printf("NonDefault "); } };

struct A {};
struct B { typedef NonDefaultType Type; };

template<typename T, typename Enable = void> struct Get_Type { 
    typedef DefaultType Type; 
};
template<typename T> struct Get_Type< T, typename T::Type > {
    typedef typename T::Type  Type; 
};

int main()
{
    Get_Type<A>::Type test1;
    Get_Type<B>::Type test2;
}

Я ожидал бы, что это распечатает "Значение по умолчанию NonDefault", но вместо этого это печатает "Значение по умолчанию По умолчанию". Мое ожидание состоит в том, что вторая строка в основном () должна соответствовать специализированной версии Get_Type, потому что B:: Тип существует. Однако этого не происходит.

Кто-либо может объяснить, что продолжается здесь и как зафиксировать его, или другой способ выполнить ту же цель?

Спасибо.

Править:

Georg дал альтернативный метод, но мне все еще любопытно на предмет того, почему это не работает. Соответственно повышение enable_if документы, способ специализировать шаблон для различных типов похож так:

template <class T, class Enable = void> 
class A { ... };

template <class T>
class A<T, typename enable_if<is_integral<T> >::type> { ... };

template <class T>
class A<T, typename enable_if<is_float<T> >::type> { ... };

Это работает, потому что <верный> enable_if имеет тип как определение типа, но enable_if <ложь> не делает.

Я не понимаю, как это отличается, чем моя версия, где вместо того, чтобы использовать enable_if я просто использую T:: Введите непосредственно. Если T:: Тип существует, не был бы это совпасть с <верным> enable_if:: введите вышеупомянутый пример и заставьте специализацию быть выбранной? И если T:: Тип не существует, не был бы это совпасть с enable_if <ложь>:: введите не существующий и вызывающий версию по умолчанию, которая будет выбрана в вышеупомянутом примере?

15
задан Frank 9 June 2010 в 20:44
поделиться

3 ответа

Чтобы ответить на ваше добавление - ваш аргумент специализации передает член typedef и ожидает, что он даст void в качестве типа. В этом нет ничего волшебного - просто используется аргумент по умолчанию. Посмотрим, как это работает. Если вы скажете Get_Type :: type , компилятор использует аргумент по умолчанию Enable , который равен void , а имя типа станет Get_Type :: type . Теперь компилятор проверяет соответствие какой-либо частичной специализации.

Список аргументов вашей частичной специализации выводится из исходного списка аргументов . Это выведет T на Foo , а затем подставит Foo во второй аргумент специализации, давая окончательный результат для вашей частичной специализации. Однако это совершенно не соответствует исходному списку аргументов !

Вам нужен способ получить тип void , как показано ниже:

template<typename T>
struct tovoid { typedef void type; };

template<typename T, typename Enable = void> struct Get_Type { 
    typedef DefaultType Type; 
};
template<typename T> 
struct Get_Type< T, typename tovoid<typename T::Type>::type > {
    typedef typename T::Type  Type; 
};

Теперь все будет работать так, как вы ожидаете. Используя MPL, вы можете использовать всегда вместо tovoid

typename apply< always<void>, typename T::type >::type
8
ответ дан 1 December 2019 в 03:42
поделиться

Вы можете сделать это, используя SFINAE:

template<class T> struct has_type {
    template<class U> static char (&test(typename U::Type const*))[1];
    template<class U> static char (&test(...))[2];
    static const bool value = (sizeof(test<T>(0)) == 1);
};

template<class T, bool has = has_type<T>::value> struct Get_Type {
    typedef DefaultType Type;
};

template<class T> struct Get_Type<T, true> { 
    typedef typename T::Type Type;
};
8
ответ дан 1 December 2019 в 03:42
поделиться

Первый шаг: перестаньте использовать "Type" и используйте mpl-стандарт "type".


BOOST_MPL_HAS_XXX_DEF(Type)

template < typename T >
struct get_type { typedef typename T::Type type; };

template < typename T >
struct calculate_type : boost::mpl::if_
<
  has_Type<T>
, get_type<T>
, boost::mpl::identity<default_type>
>::type {}

typedef calculate_type<A>::type whatever;

Если бы вы использовали "type" вместо "Type" в своих метафункциях, вам бы не требовалось преобразовывать fetcher "get_type", и вы могли бы просто возвращать T в этом случае.

4
ответ дан 1 December 2019 в 03:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: