所有子类的c ++模板特化

时间:2012-01-08 03:24:32

标签: c++ templates template-specialization

我需要创建一个这样的模板函数:

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

我也可以想象使用模板特化来做它...但我从未见过超类的所有子类的模板特化。我不想为每个子类重复专门化代码

5 个答案:

答案 0 :(得分:14)

你可以做你想做的事,但不能做你想做的事!您可以将std::enable_ifstd::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_ifstd::is_base_of(在<type_traits>中声明)都是C ++ 2011中的新功能。如果需要使用C ++ 2003编译器进行编译,可以使用Boost中的实现(需要将命名空间更改为boost并包括"boost/utility.hpp""boost/enable_if.hpp"而不是相应的标准标题)。或者,如果你不能使用Boost,这两个类模板都可以很容易地实现。

答案 1 :(得分:4)

我会将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_typestd::false_type只是typedef,定义为:

typedef integral_constant<bool, true>  true_type;
typedef integral_constant<bool, false> false_type;

答案 2 :(得分:3)

我喜欢这种清晰的风格:

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

答案 3 :(得分:1)

问题是,实际上您无法在C ++ 17中执行以下操作:

template<T>
struct convert_t {
    static auto convert(T t) { /* err: no specialization */ }
}
template<T>
struct convert_t<T> {
  // T should be subject to the constraint that it's a subclass of X
}

但是,有两种方法可以使编译器根据涉及标签分派和SFINAE的类层次结构选择正确的方法。

让我们从标签分发开始。此处的关键是所选标签是指针类型。如果BA继承,则为类型A*的值选择B*的重载:

#include <iostream>
#include <type_traits>

struct type_to_convert {
    type_to_convert(int i) : i(i) {};
    type_to_convert(const type_to_convert&) = delete;
    type_to_convert(type_to_convert&&) = delete;
    int i;
};

struct X {
    X(int i) : i(i) {};
    X(const X &) = delete;
    X(X &&) = delete;
public:
    int i;
};
struct Y : X {
    Y(int i) : X{i + 1} {}
};
struct A {};

template<typename>
static auto convert(const type_to_convert &t, int *) {
    return t.i;
}
template<typename U>
static auto convert(const type_to_convert &t, X *) {
    return U{t.i}; // will instantiate either X or a subtype
}

template<typename>
static auto convert(const type_to_convert &t, A *) {
    return 42;
}

template<typename T /* requested type, though not necessarily gotten */>
static auto convert(const type_to_convert &t) {
    return convert<T>(t, static_cast<T*>(nullptr));
}

int main() {
    std::cout << convert<int>(type_to_convert{5}) << std::endl;
    std::cout << convert<X>(type_to_convert{6}).i << std::endl;
    std::cout << convert<Y>(type_to_convert{6}).i << std::endl;
    std::cout << convert<A>(type_to_convert{-1}) << std::endl;
    return 0;
}

另一种选择是将SFINAE与enable_if一起使用。这里的关键是,尽管问题开头的代码段无效,但该专业化不是:

template<T, typename = void>
struct convert_t {
    static auto convert(T t) { /* err: no specialization */ }
}
template<T>
struct convert_t<T, void> {
}

因此,只要确保在任何给定点上只有一个有效,我们的专业就可以保留完全通用的第一个参数。为此,我们需要形成相互排斥的条件。示例:

template<typename T /* requested type, though not necessarily gotten */,
         typename = void>
struct convert_t {
    static auto convert(const type_to_convert &t) {
        static_assert(!sizeof(T), "no conversion");
    }
};

template<>
struct convert_t<int> {
    static auto convert(const type_to_convert &t) {
        return t.i;
    }
};

template<typename T>
struct convert_t<T, std::enable_if_t<std::is_base_of_v<X, T>>> {
    static auto convert(const type_to_convert &t) {
        return T{t.i}; // will instantiate either X or a subtype
    }
};

template<typename T>
struct convert_t<T, std::enable_if_t<std::is_base_of_v<A, T>>> {
    static auto convert(const type_to_convert &t) {
        return 42; // will instantiate either X or a subtype
    }
};

template<typename T>
auto convert(const type_to_convert& t) {
    return convert_t<T>::convert(t);
}

注意:问题文本中的特定示例可以用constexpr解决,尽管:

template<typename T>
void foo(T a) {
   if constexpr(std::is_base_of_v<Bar, T>) 
      // do this
   else
      // do something else
}

答案 4 :(得分:0)

我知道已经回答了这个问题,但是没有人提到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;
}