重载泛型类型的函数与给定类型及其子类

时间:2016-06-02 16:29:43

标签: c++ templates c++11 sfinae typetraits

我正在尝试编写一对重载函数,一个必须调用指向不是B的类型或B的子类型的指针,第二个必须调用指向B的子节点和B的子节点。起初我尝试使用B模板的专门化,但这对于B的派生类没有用。所以我查找了SFINAE和enable_if等但是无法使它工作

通用功能的签名是

(1)template<typename T> int f(T *t)

对于另一个,我尝试使用enable_ifis_base_of,如下所示:

(2)template<typename T> int f(typename enable_if<is_base_of<B, T>::value, T>::type *t)

但总是(1)被召唤。我试图通过否定(2)代替(1):

(1b)template<typename T> int f(typename enable_if<!is_base_of<B, T>::value, T>::type *t)

现在我得到所有Ts的错误,无论他们是否是(B的孩子)。

我做错了什么?解决方案是什么?

测试代码如下:

#include <type_traits>
#include <iostream>

using namespace std;

class B {};
class D : public B {};
class C {};

// (1)
/* template<typename T>
int f(T *t)
{ cout << "T\n"; } */

// (1b)
template<typename T>
int f(typename enable_if<!is_base_of<B, T>::value, T>::type *t)
{ cout << "T\n"; }

// (2)
template<typename T>
int f(typename enable_if<is_base_of<B, T>::value, T>::type *t)
{ cout << "B\n"; }

int main()
{
  B b;
  D d;
  C c;
  f(&b);    // Want B; get T with (1), dont compile with (1b)
  f(&d);    // Want B; get T with (1), dont compile with (1b)
  f(&c);    // Want T; get T with (1), dont compile with (1b)
  return 0;
}

3 个答案:

答案 0 :(得分:4)

将SFINAE移动到我们可以使用的模板参数

// if not B or a descendant
template<typename T, typename enable_if<!is_base_of<B, T>::value>::type* = nullptr>
void f(T *t)
{ cout << "T\n"; }

// only if B or a descendant
template<typename T, typename enable_if<is_base_of<B, T>::value>::type* = nullptr>
void f(T *t)
{ cout << "B\n"; }

然后针对

运行它
int main()
{
  B b;
  D d;
  C c;
  f(&b);    // Want B; get T with (1), dont compile with (1b)
  f(&d);    // Want B; get T with (1), dont compile with (1b)
  f(&c);    // Want T; get T with (1), dont compile with (1b)
  return 0;
}

我们得到了

B
B
T

Live Example

由于你没有任何return语句,我也把函数设为void函数。

答案 1 :(得分:3)

typename enable_if<!is_base_of<B, T>::value, T>::type不可扣除,因此您必须明确致电:

f<B>(&b);    // Want B;
f<D>(&d);    // Want B;
f<C>(&c);    // Want T;

Demo

要进行推理,您可以使用经典方式之一SFINAE:return type

// (1b)
template<typename T>
enable_if_t<!is_base_of<B, T>::value>
f(T* t)
{ cout << "T\n"; }

// (2)
template<typename T>
enable_if_t<is_base_of<B, T>::value>
f(T* t)
{ cout << "B\n"; }

Demo

或作为模板参数:

// (1b)
template<typename T, enable_if_t<!is_base_of<B, T>::value>* = nullptr>
void f(T* t)
{ cout << "T\n"; }

// (2)
template<typename T, enable_if_t<is_base_of<B, T>::value>* = nullptr>
void f(T* t)
{ cout << "B\n"; }

Demo

答案 2 :(得分:2)

一个非常简单的解决方法是再次将指针作为第二个函数参数传递,用于区分两个版本

template<typename T>
void fImpl(T* t, const void*) { 
   std::cout << "general";
}

template<typename T>
void fImpl(T *b, const B*) {
   std::cout << "specific";
}

template<typename T>
void f(T *t) { 
   fImpl(t, t);
}