Шаблон специализации для использования типа по умолчанию, если член класса typedef не существует - PullRequest
15 голосов
/ 09 июня 2010

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

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;
}

Я бы ожидал, что это напечатает «Default NonDefault», но вместо этого он напечатает «Default Default».Я ожидаю, что вторая строка в main () должна соответствовать специализированной версии Get_Type, потому что B :: Type существует.Однако этого не происходит.

Может кто-нибудь объяснить, что здесь происходит и как это исправить, или другой способ достижения той же цели?

Спасибо.

Изменить:

Георг дал альтернативный метод, но мне все еще интересно, почему это не работает.Согласно документам boost 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 имеет тип как typedef, а enable_if нет.

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

Ответы [ 3 ]

8 голосов
/ 10 июня 2010

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

Список аргументов вашей частичной специализации <T, typename T::Type> выводится из исходного списка аргументов <Foo, void>. Это выведет T в Foo и затем подставит это Foo во второй аргумент специализации, в результате чего будет получен конечный результат <Foo, NonDefaultType> для вашей частичной специализации. Это, однако, не соответствует исходному списку аргументов <Foo, void> вообще!

Вам нужен способ получения типа 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, вы можете использовать always вместо tovoid

typename apply< always<void>, typename T::type >::type
8 голосов
/ 09 июня 2010

Вы можете сделать это, используя 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;
};
4 голосов
/ 09 июня 2010

Первый шаг: прекратите использовать «Тип» и используйте стандартный «тип» mpl.


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;

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...