Переключить пройденный тип из шаблона

19

Возможно ли в C ++ проверить тип, переданный в функцию шаблона? Например:

template <typename T>
void Foo()
{
   if (typeof(SomeClass) == T)
      ...;
   else if (typeof(SomeClass2) == T)
      ...;
}
    
задан Ockonal 23.12.2010 в 21:50
источник

4 ответа

29

Да, это ... но он, вероятно, не будет работать так, как вы ожидаете.

template < typename T >
void foo()
{
  if (is_same<T,SomeClass>::value) ...;
  else if (is_same<T,SomeClass2>::value) ...;
}

Вы можете получить is_same из std:: или boost:: в зависимости от вашего желания / компилятора. Первый только в C ++ 0x.

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

Чтобы решить эту проблему, вам нужно сделать что-то совсем другое. Я бы рекомендовал отправку тегов:

struct v1_tag {};
struct v2_tag {};

template < typename T > struct someclass_version_tag;
template < > struct someclass_version_tag<SomeClass> { typedef v1_tag type; };
template < > struct someclass_version_tag<SomeClass2> { typedef v2_tag type; };

void foo(v1_tag) { ... }
void foo(v2_tag) { ... }
template < typename T > void foo()
{
  typedef typename someclass_version_tag<T>::type tag;
  foo(tag());
}

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

    
ответ дан Crazy Eddie 23.12.2010 в 22:01
источник
  • Можно ли возвращать разные типы на основе version_tag? –  Boying 24.02.2017 в 10:03
  • Конечно. Вам нужно каким-то образом запросить информацию, если вы застряли на C ++ 03, или используйте auto в C ++ 11 и выше. До тех пор, пока вы не пытаетесь вернуть разные типы в рамках одного и того же экземпляра, у вас не будет проблем. –  Crazy Eddie 24.02.2017 в 18:45
  • теперь с C ++ 17, вы можете использовать выражения constexpr if, чтобы сделать это тривиально –  xaxxon 09.09.2017 в 01:48
10

Если вы хотите сделать что-то конкретное, основанное на типе, специализируйте шаблон:

template <typename T>
void Foo() { }

template <>
void Foo<SomeClass>() { }

template <> 
void Foo<SomeClass2>() { }

// etc.

(На самом деле, вы не хотите специализировать шаблон функции, это только для изложения. Вам нужно либо перегрузить шаблон, либо добавить его в специализированный шаблон шаблона. как избежать специализированных шаблонов функций, прочитайте Herb Sutter> Почему не специализировать шаблоны функций? )

    
ответ дан James McNellis 23.12.2010 в 21:56
источник
2

Нет, однако вы можете использовать частичную специализацию:

template<typename T>
struct Bar { static void foo(); };
template<typename T>
template<> inline void Bar<T>::foo() {
//generic
}
template<> inline void Bar<int>::foo() {
//stuff for int
}
template<> inline void Bar<QString>::foo() {
//QString
}

Изменить Да с характерными чертами, однако это не нужно. Изменить 2 пример type_traits.

#include <type_traits>
template<typename T> void foo() {
    using std::is_same;
    if<is_same<T, T2>::value || is_same<T, T1>::value) { 
        /* stuff */
    }
}
    
ответ дан OneOfOne 23.12.2010 в 21:59
источник
  • Что делать, если я хочу сделать что-то вроде if (is_same <T, T1> || is_same <T, T2>)? –  Dec 23 '10 at 21:01 23.12.2010 в 22:01
  • Затем вам нужно использовать черты типов #include <type_traits> и std :: is_same <T, T2> :: значение, если ваш компилятор поддерживает C ++ 0x (VS2010 / любой недавний gcc) или просто использует boost для старого компилятора совместимость. –  OneOfOne 23.12.2010 в 22:06
  • Это не частичная специализация, это полная специализация. Есть довольно значительная разница. Наверное, наиболее значительным является то, что было бы невозможно сделать частичную специализацию таким образом, поскольку это не допускается с функциями любого рода. –  Crazy Eddie 23.12.2010 в 22:22
2

Да. Вам нужно будет использовать свойства типа . Например:

#include <boost/type_traits/is_same.hpp>

template <typename T>
void Foo ()
{
   if ((boost::is_same<T, SomeClass>::value))
      ...;
   else if ((boost::is_same<T, SomeClass2>::value))
      ...;
}

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

Кроме того, вы можете использовать enable_if / disable_if для условного включения / отключения некоторые функции / методы. Объединение этого с типом признаков позволит, например, использовать одну функцию для одного набора типов и другую функцию для другого набора типов.

    
ответ дан user405725 23.12.2010 в 21:58
источник
  • Ваш код не будет компилироваться. Вам нужно либо создать экземпляр is_same как переменную, либо вам нужно получить доступ к его определению внутреннего значения. –  Crazy Eddie 23.12.2010 в 22:08
  • @Noah: Правильно. Это был быстрый и грязный намек :-) Я изменил его. –  23.12.2010 в 22:13
  • Возможно ли это сделать для набора значений? Например, сделать что-то еще для каждого значения перечисления? –  paulm 07.01.2014 в 10:54
  • @paulm: Я не понимаю, почему –  Jan 7 '14 at 13:28 07.01.2014 в 14:28