C ++ шаблонная специализация для всех подклассов - PullRequest
9 голосов
/ 08 января 2012

Мне нужно создать шаблонную функцию следующим образом:

template<typename T>
void foo(T a)
{
   if (T is a subclass of class Bar)
      do this
   else
      do something else
}

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

Ответы [ 4 ]

14 голосов
/ 08 января 2012

Вы можете делать то, что вы хотите, но не так, как вы пытаетесь это сделать!Вы можете использовать std::enable_if вместе с std::is_base_of:

#include <iostream>
#include <utility>
#include <type_traits>

struct Bar { virtual ~Bar() {} };
struct Foo: Bar {};
struct Faz {};

template <typename T>
typename std::enable_if<std::is_base_of<Bar, T>::value>::type
foo(char const* type, T) {
    std::cout << type << " is derived from Bar\n";
}
template <typename T>
typename std::enable_if<!std::is_base_of<Bar, T>::value>::type
foo(char const* type, T) {
    std::cout << type << " is NOT derived from Bar\n";
}

int main()
{
    foo("Foo", Foo());
    foo("Faz", Faz());
}

Поскольку этот материал становится все более распространенным, люди обсуждали наличие какого-то static if, но до сих пор оно не появилось.

Оба std::enable_if и std::is_base_of (объявленные в <type_traits>) являются новыми в C ++ 2011.Если вам нужно скомпилировать компилятор C ++ 2003, вы можете использовать их реализацию из Boost (вам нужно изменить пространство имен на boost и включить "boost/utility.hpp" и "boost/enable_if.hpp" вместо соответствующихстандартные заголовки).В качестве альтернативы, если вы не можете использовать Boost, оба эти шаблона класса могут быть реализованы довольно легко.

4 голосов
/ 08 января 2012

Я бы использовал std::is_base_of вместе с локальным классом как:

#include <type_traits>  //you must include this: C++11 solution!

template<typename T>
void foo(T a)
{
   struct local
   {
        static void do_work(T & a, std::true_type const &)
        {
            //T is derived from Bar
        }
        static void do_work(T & a, std::false_type const &)
        {
            //T is not derived from Bar
        }
   };

   local::do_work(a, std::is_base_of<Bar,T>());
}

Обратите внимание, что std::is_base_of происходит от std::integral_constant, поэтому объект первого типа может быть неявно преобразован в объект последнего типа, что означает, что std::is_base_of<Bar,T>() преобразуется в std::true_type или std::false_type в зависимости от значение T. Также обратите внимание, что std::true_type и std::false_type - это не что иное, как typedefs, определяемые как:

typedef integral_constant<bool, true>  true_type;
typedef integral_constant<bool, false> false_type;
3 голосов
/ 08 января 2012

Мне нравится этот чистый стиль:

void foo_detail(T a, const std::true_type&)
{
    //do sub-class thing
}

void foo_detail(T a, const std::false_type&)
{
    //do else
}

void foo(T a)
{
    foo_detail(a, std::is_base_of<Bar, T>::value);
}
0 голосов
/ 18 октября 2018

Я знаю, что на этот вопрос ответили, но никто не упомянул, что std :: enable_if может использоваться в качестве второго параметра шаблона, например:

#include <type_traits>

class A {};
class B: public A {};

template<class T, typename std::enable_if<std::is_base_of<A, T>::value, int>::type = 0>
int foo(T t)
{
    return 1;
}
...