【问题标题】:How to tell if class contains a certain member function in compile time [duplicate]如何在编译时判断类是否包含某个成员函数[重复]
【发布时间】:2011-04-27 05:37:09
【问题描述】:

可能重复:
Is it possible to write a C++ template to check for a function's existence?

假设有 2 个类:

struct A{ int GetInt(){ return 10; } };
struct B{ int m; };

我想在下面的函数中使用 A 或 B 类型的对象

tempate< typename T >
int GetInt( const T & t )
{
   //if it's A, I'll call: return t.GetInt();
   //if its' B, I'll call: return t.m;
}

现在,因为有一大堆类,有些包含GetInt(),有些没有,我不想为每种类型写专门化,我只想通过'包含GetInt( ) 或不在编译时',我应该怎么做?

【问题讨论】:

  • 也许你应该研究一下虚函数和多态性。
  • 除非您假设您的非GetInt 实例化类都有一个名为int 的成员m,否则我不明白如何在没有专门化的情况下扩展它。你的函数模板的默认实现是什么?
  • 你不能让这个函数工作,因为GetInt不是const,但t是。

标签: c++ compile-time member-functions


【解决方案1】:

Substitution Failure Is Not An Error, or more compactly, SFINAE

但在您的特定情况下,您不需要 SFINAE、虚拟会员或任何类似的东西。

你只需要一个普通的重载函数。

int GetInt(A& t) { return t.GetInt(); }
int GetInt(const B& t) { return t.m; }

如果有需要在不同版本之间共享的代码,请对其进行重构,以便有一个调用重载内联函数的模板,所有类型特定的行为都在内联函数中,所有共享行为都在模板中。

对于您“我有很多很多类”的需求,SFINAE 或多或少看起来像这样:

template<typename T>
int GetInt(const T& t, int (T::*extra)() const = &T::GetInt)
{
    return t.GetInt();
}

template<typename T>
auto GetInt(const T& t) -> decltype(t.m)
{
    return t.m;
}

编辑:SFINAE 的现实要丑陋得多,至少在 C++0x 出现之前是这样。事实上,它开始看起来和 GMan 的回答一样糟糕。

struct A{ int GetInt() const { return 10; } };
struct B{ int m; };

template<typename T, int (T::*extra)() const>
struct has_mfunc
{
    typedef int type;
};

template<typename T>
typename has_mfunc<T, &T::GetInt>::type GetInt(const T& t)
{
    return t.GetInt();
}

template<typename T, typename U, U (T::*extra)>
struct has_field
{
    typedef U type;
};

template<typename T>
typename has_field<T, int, &T::m>::type GetInt(const T& t)
{
    return t.m;
}

int main(void)
{
   A a;
   B b;
   b.m = 5;
   return GetInt(a) + GetInt(b);
}

【讨论】:

  • 您的意思是 t.*extra() 而不是 t.GetInt()?我看不到如何使用“额外”参数
  • @Chubsdad:额外的参数仅用于导致替换失败,以便从匹配列表中删除重载之前模板主体被实例化(此时编译器在尝试查找 B::GetInt) 时会出错。
  • 值得一提的是 decltype 不在当前标准中... :-/。此外,问题非常清楚“我不想为每种类型编写专业化” - 我会排除重载建议。
  • @Tony:很容易使用指向成员的指针来检查t.m,只是想,因为我已经给出了一个例子,所以我会举一个例子decltype 也是。
  • @Tony:显然默认参数不会得到解决,直到 SFINAE 启动为时已晚。使用 C++0x,使用 decltype 的后缀返回类型将使这变得容易。在那之前,一切都是一团糟。
【解决方案2】:

here 窃取,并假设您修复了代码,因此GetInt 是 const,我们得到:

HAS_MEM_FUNC(GetInt, has_GetInt);

template <bool B>
struct bool_type
{
    static const bool value = B;
};

typedef bool_type<true> true_type;
typedef bool_type<false> false_type;

namespace detail
{
    template <typename T>
    int get_int(const T& pX, true_type)
    {
        return pX.GetInt();
    }

    template <typename T>
    int get_int(const T& pX, false_type)
    {
        return pX.m;
    }
}

template <typename T>
int get_int(const T& pX)
{
    return detail::get_int(pX,
                            has_GetInt<T, int (T::*)() const>::value);
}

不过,这是非常糟糕的设计。您应该解决问题而不是应用补丁。

【讨论】:

  • 太棒了,谢谢你替我偷东西!
【解决方案3】:

从技术上讲,它只涉及一些模板奥秘,你可以通过谷歌搜索找到。 has_member 之类的。即兴发挥,在检测代码中,如果我要编写这样的代码,我只是从相关类中进行假派生,并检查派生类成员的大小。

但是,不要那样做。

还要做什么取决于。但似乎你的类符合两个不同的“模式”,可以这么说,没有这些模式可以通过类型系统获得(比如,这些类似乎不是从两个基类 A 和 B 派生的)。然后一个选项是引入一个特征模板,它告诉您包装器模板参数 T 是模式 A 还是 B。为每个与默认值不同的相关类专门化特征。选择默认值以减少工作量。

干杯,

【讨论】:

    【解决方案4】:

    这正是继承的用途。您可以在运行时轻松地使用 dynamic_cast 来处理问题类型。例如,您可以定义一个名为 HasGetInt 的抽象基类,并从中派生需要该功能的类,而无需重新发明轮子。

    【讨论】:

    • 在模板类型中,他可以做任何他想做的事情,直到它被一个类型实例化。他当然可以重载模板函数类型,但我经常发现这些类型的解决方案可以通过接口模型更好地解决。
    • 谢谢大家,但代码是遗留代码,我尽量不更改现有代码,而是在其之上添加一些实用功能以使生活更轻松。
    猜你喜欢
    • 2014-10-10
    • 2021-08-19
    • 1970-01-01
    • 2018-08-10
    • 2011-05-10
    • 1970-01-01
    • 2011-03-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多