Особенности рекурсивного типа C ++

Я пытаюсь реализовать шаблонный класс (названный здесь Get<>), который, учитывая структуру H, тип Get<H>::type является самим H, если квалифицированный-идентификатор H::der не существует, и Get<H::der>::type в противном случае . Я не могу понять, что не так со следующим кодом:

#include <iostream>
#include <typeinfo>
using namespace std;

template<class U, class V = void>
struct Get
{
  static const char id = 'A';
  typedef U type;
};

template<class U>
struct Get<U,typename U::der>
{
  static const char id = 'B';
  typedef typename Get<typename U::der>::type type;
};

struct H1
{ };
struct H2
{ typedef double der; };
struct H3
{ typedef void der; };
struct H4
{ typedef H2 der; };

void print(char id, const char* name)
{
  cout << id << ", " << name << endl;
}
int main(int , char *[])
{
  print(Get<H1>::id, typeid(Get<H1>::type).name()); // prints "A, 2H1", OK
  print(Get<H2>::id, typeid(Get<H2>::type).name()); // prints "A, 2H2", why?
  print(Get<H3>::id, typeid(Get<H3>::type).name()); // prints "B, v"  , OK
  print(Get<H4>::id, typeid(Get<H4>::type).name()); // prints "A, 2H4", why?
}

Мне нужна помощь, чтобы заставить этот код вести себя должным образом. В частности, я бы хотел, чтобы Get< H2 >::type был равен double, и то же самое для Get< H4 >::type.


person montefuscolo    schedule 24.11.2012    source источник
comment
Уловка litb tovoid в этом ответе решает вашу проблему: stackoverflow.com/a/3009891/245265   -  person Emile Cormier    schedule 24.11.2012
comment
хороший трюк, не знал этого.   -  person ipc    schedule 24.11.2012


Ответы (2)


Хотя я даю ответ +1 на @ipc, и это очень хорошо для компиляторов с поддержкой C ++ 11, для компиляторов C ++ 03 вам следует использовать другой подход, поскольку значение по умолчанию для аргументов шаблона функции не поддерживается в C ++ 03. Итак, у меня есть этот код:

template<class U, class V = void>
struct Get
{
    static const char id = 'A';
    typedef U type;
};

template< class T >
struct is_type {
    static const bool value = true;
};

template<class U>
struct Get<U,
    typename std::tr1::enable_if<is_type<typename U::der>::value, void>::type>
{
    static const char id = 'B';
    typedef typename Get<typename U::der>::type type;
};
person BigBoss    schedule 24.11.2012
comment
Если вы используете boost::enable_if (std::enable_if - это C ++ 11), typename boost::enable_if<typename U::der>::type тоже должен работать и делает is_type вспомогательный класс излишним. - person ipc; 24.11.2012
comment
@ipc На самом деле я использую boost::enable_if в своем реальном проекте, но это не так, поскольку условие boost::enable_if должно быть логической константой mpl. правильный код - boost::enable_if<is_type<U::der> >. И помните, что std::enable_if принадлежат TR1, а не C ++ 11. - person BigBoss; 24.11.2012
comment
ок, ты прав. Но std::enable_if по-прежнему C ++ 11, поскольку встраивание std::tr1 в std нестандартно. - person ipc; 24.11.2012
comment
@ipc Извините, я не понимаю, документ, к которому вы обратились, безусловно, описывает аргумент Cond и говорит, что это должен быть тип с вложенной константой bool с именем value (это логическая константа в MPL). 2 Шаблоны enable_if - person BigBoss; 24.11.2012
comment
@ipc спасибо за комментарий, я исправляю нестандартное использование std::tr1::enable_if в пространстве имен std - person BigBoss; 24.11.2012

В шаблоне Get<> есть параметр шаблона по умолчанию - это очень опасно. В зависимости от того, равно ли V int, void или double, вы получите разные результаты. Вот что происходит:

Get<H2>::type занимает Get<H2, void> на первом месте (с id='A'). Теперь нужно проверить, есть ли у этого специализация. Ваш B - это Get<U,typename U::der>, который становится Get<U, double>. Но это не совпадает с Get<H2, void>, поэтому выбрано A. С Get<H2>::type дела становятся интереснее. Тогда вариант B также Get<U, void> и обеспечивает лучшее соответствие. Однако этот подход не может работать для всех типов.

Вот как я бы реализовал Get:

template<class U>
class Get
{
  template <typename T, typename = typename T::der>
  static typename Get<typename T::der>::type test(int);
  template <typename T>
  static T test(...);
public:
  typedef decltype(test<U>(0)) type;
};
person ipc    schedule 24.11.2012