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

Overloading a function for a generic type vs for a given type and its children

本文关键字:函数 类型 泛型类型 重载      更新时间:2023-10-16

我正在尝试编写一对重载函数,一个必须为指向非 B 或 B 的子类型的指针调用,另一个必须调用指向 B 和 B 的子级的指针。起初,我尝试将模板的专用化用于 B,但这不适用于 B 的派生类。所以我查找了SFINAE和enable_if等,但无法使其工作。

泛型函数的签名为

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

对于另一个,我尝试使用enable_if,并像这样is_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)

现在我收到所有 T 的错误,无论它们是否是 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 << "Tn"; } */
// (1b)
template<typename T>
int f(typename enable_if<!is_base_of<B, T>::value, T>::type *t)
{ cout << "Tn"; }
// (2)
template<typename T>
int f(typename enable_if<is_base_of<B, T>::value, T>::type *t)
{ cout << "Bn"; }
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;
}

将 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 << "Tn"; }
// 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 << "Bn"; }

然后运行它

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

现场示例

我还使函数无效函数,因为您没有任何返回语句。

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;

演示

为了可推导,您可以通过经典方式之一使用 SFINAE:返回类型

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

演示

或作为模板参数:

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

演示

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

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