【问题标题】:Overloading a function for a generic type vs for a given type and its children重载泛型类型与给定类型及其子类型的函数
【发布时间】:2016-06-02 16:29:42
【问题描述】:

我正在尝试编写一对重载函数,一个必须为指向非 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)

现在我得到所有 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 << "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;
}

【问题讨论】:

  • is_base_of&lt;B, T&gt;::value, T&gt;::type 不会神奇地推断出T 并将其用作type

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


【解决方案1】:

将 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

由于您没有任何返回语句,因此我还将函数设为无效函数。

【讨论】:

  • 谢谢,这行得通!但是,有没有办法让它在没有默认模板参数的情况下工作?我的解决方案还需要在 c++11 之前使用,我使用的是 is_base_ofenable_if 的变体,但现在它对默认模板参数无效。
  • @chengiz 我不确定。当我使用 SFINAE 时,这是我最满意的方式。我还在学习它。
【解决方案2】:

typename enable_if&lt;!is_base_of&lt;B, T&gt;::value, T&gt;::type 是不可演绎的,所以你必须显式调用:

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

Demo

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

// (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

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

    • 不错。这对于 OP 来说可能是最好的,因为它们实际上没有 C++11
    • 天才!非常适合我。
    • 你甚至可以简化它,在这里同时依赖static_cast和重载(以this为例)。
    猜你喜欢
    • 1970-01-01
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-23
    • 2018-06-12
    • 2019-06-04
    • 2011-10-09
    相关资源
    最近更新 更多