Отключить функции внутри шаблонного класса - PullRequest
5 голосов
/ 14 сентября 2011

Я пытаюсь отключить некоторые функции внутри простого класса шаблона.Функции, которые должны быть удалены, зависят от того, имеет ли аргумент шаблона определенные typedefs.

Пример сводится к следующему:

template<typename T>
struct Foo
{
  typename T::Nested foo() { return typename T::Nested(); }
  int bar() { return 1; }
};


struct NoNested
{
};

struct WithNested
{
  typedef int Nested;
};

int main()
{
  Foo<WithNested> fwn;
  fwn.foo();
  fwn.bar();

  Foo<NoNested> fnn;
  //fnn.foo();
  fnn.bar();
}

Однако это дает мне ошибку стиля error: no type named ‘Nested’ in ‘struct NoNested’ на gccи clang ++ (обратите внимание на старые версии обоих).

Есть ли простой способ удалить foo, когда typedef T::Nested не завершается?(Кроме специализации шаблона класса Foo<T>, как в реальном коде, у меня есть это для примерно 5 функций с разными определениями типов .. что приведет к 2 ^ 5 различным специализациям)

РЕДАКТИРОВАТЬ: Поскольку некоторые просили мотивировать желание сделать это: я хотел бы создать что-то вроде FSM времени компиляции для использования в DSL.

Я хотел бы иметь возможность сделатьthis

struct StateA;
struct StateB;
struct StateC;

struct StateA
{
  typedef StateB AfterNext;
};

struct StateB
{
   typedef StateA AfterPrev;
   typedef StateC AfterNext;
};

struct StateC
{
   typedef StateB AfterPrev;
};

template<typename T>
struct FSM
{
   FSM<typename T::AfterNext> next() { return FSM<T::AfterNext>(); };
   FSM<typename T::AfterPrev> prev() { return FSM<T::AfterPrev>(); };
};

, так что

FSM<StateA>().next().prev().next().next();

компилируется, но

FSM<StateA>().next().prev().prev();

завершается неудачей.

Обратите внимание, что в действительности было бы больше переходовфункций, чем эта, функции перехода на самом деле что-то делают, и FSM будет хранить некоторое состояние.

ОБНОВЛЕНИЕ : я создал правильных примеров , используя методы, которыебыли даны до сих пор.Ответы различаются по сложности, и, хотя метод посетителей - это тот, который я, вероятно, в конечном итоге буду использовать (поскольку он наиболее прост), мое решение (самое сложное) - единственное, которое фактически удаляет функцию.

Ответы [ 5 ]

4 голосов
/ 14 сентября 2011

Вы можете использовать специализацию шаблона класса.Если у вас есть несколько функций, вы можете переместить каждую функцию в базовый класс и специализировать каждый базовый класс.

1 голос
/ 14 сентября 2011

Можно выбрать тип T::Nested, если он существует, в противном случае void, следующим образом.

Выбор по умолчанию: void:

template<class T, class = void>
struct NestedReturn
{
  typedef void type;
};

Aшаблон, который всегда возвращает void, какой бы тип вы ему ни указали:

template<class T>
struct Void
{
  typedef void type;
};

Специализация для типов с вложенным классом Nested от SFINAE.Обратите внимание, что typename Void<typename T::Nested>::type всегда void, чтобы соответствовать второму параметру по умолчанию void в базовом шаблоне:

template<class T>
struct NestedReturn<T, typename Void<typename T::Nested>::type>
{
  typedef typename T::Nested type;
};

И теперь мы используем его.Обратите внимание, что foo() на самом деле не удаляется при отсутствии T::Nested, но его создание вызывает ошибку.

template<typename T>
struct Foo
{
  typename NestedReturn<T>::type foo() { return typename T::Nested(); }
  int bar() { return 1; }
};


struct NoNested
{
};

struct WithNested
{
  typedef int Nested;
};

int main()
{
  Foo<WithNested> fwn;
  fwn.foo();
  fwn.bar();

  Foo<NoNested> fnn;
  //fnn.foo();
  fnn.bar();
}

Я подозреваю, что при использовании параметров шаблона функции по умолчанию можно будет удалить foo()правильно используя SFINAE, но это возможно только в C ++ 11 (непроверенные догадки):

template<typename T>
struct Foo
{
  template<class N = T::Nested>
  N foo() { return N(); }
  int bar() { return 1; }
};
1 голос
/ 14 сентября 2011

Вы можете добавить вложенную typedef для каждого класса, так что компиляция завершится неудачно только при создании экземпляра функции.

struct null_type;  //an incomplete type, you could use a more descriptive name for your particular problem

template<typename T>
struct Foo
{
  typename T::Nested foo() { return typename T::Nested(); }
  int bar() { return 1; }
};


struct NoNested
{
   typedef null_type Nested;
};

struct WithNested
{
  typedef int Nested;
};

int main()
{
  Foo<WithNested> fwn;
  fwn.foo();
  fwn.bar();

  Foo<NoNested> fnn;
  //fnn.foo();  //attempt to use incomplete type when used
  fnn.bar();
}
1 голос
/ 14 сентября 2011

Попробуйте сделать функцию foo для самого шаблона. Он будет скомпилирован только при вызове, поэтому вы получите ошибку только тогда, когда попробуете вызвать ее с NoNested class.

0 голосов
/ 14 сентября 2011

Вот как я думаю, я могу решить это.Это вдохновлено комментариями пользователя 763305.Требуется 2 * N специализации, а не 2 ^ N.

template <typename T>
struct has_nested {
  // Variables "yes" and "no" are guaranteed to have different sizes,
  // specifically sizeof(yes) == 1 and sizeof(no) == 2.
  typedef char yes[1];
  typedef char no[2];

  template <typename C>
    static yes& test(typename C::Nested*);

  template <typename>
    static no& test(...);

  // If the "sizeof" the result of calling test<T>(0) would be equal to the sizeof(yes),
  // the first overload worked and T has a nested type named type.
  static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};



template<typename T>
struct FooBase
{
  int bar() { return 1; }
};

template<typename T, bool>
struct FooImpl : public FooBase<T>
{
};

template<typename T>
struct FooImpl<T,true> : public FooBase<T>
{
  typename T::Nested foo() { return typename T::Nested(); }
};


template<typename T>
struct Foo : public FooImpl<T, has_nested<T>::value >
{
};


struct NoNested
{
};

struct WithNested
{
  typedef int Nested;
};

int main()
{
  Foo<WithNested> fwn;
  fwn.foo();
  fwn.bar();

  Foo<NoNested> fnn;
  //fnn.foo();
  fnn.bar();

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