关于c ++:如何在编译时判断类是否包含某个成员函数

How to tell if class contains a certain member function in compile time

本问题已经有最佳答案,请猛点这里访问。

Possible Duplicate:
Is it possible to write a C++ template to check for a function's existence?

假设有两个类:

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

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

1
2
3
4
5
6
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()或不在编译时"来区分它们,我该怎么做呢?


替换失败不是一个错误,或者更紧凑地说,不是一个错误。

但在您的特定情况下,您不需要sfinae、虚拟成员或类似的任何花哨内容。

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

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

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

对于您的"我有许多课程"需求,sfinae或多或少会像这样:

1
2
3
4
5
6
7
8
9
10
11
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;
}

编辑:SFIEAE的现实更加丑陋,至少在C++0X出现之前。事实上,它开始看起来和GMAN的答案一样糟糕。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);
}


从这里窃取,假设您修复了代码,所以GetInt是常量,我们得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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);
}

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


这正是继承的目的。您可以在运行时轻松地使用"动态"类型的问题。例如,您可以定义一个名为hasGetInt的抽象基类,并从中派生出需要该函数的类,而不是重新创建控制盘。


从技术上讲,它只涉及一些模板arcana,您可以通过谷歌搜索找到,例如has_member等。在检测代码中,如果我写这样的代码,我只需要从所讨论的类中伪派生,并检查派生类成员的大小。

但是,不要这样做。

还有什么事要看情况而定。但是,您的类似乎符合两种不同的"模式",也就是说,如果没有通过类型系统提供这些模式(例如,类似乎不是从两个基类A和B派生的)。然后,一个选项是引入一个特性模板,告诉您包装器模板参数t是模式a还是模式b。专门化每个与默认值不同的相关类的特性。选择默认值以最小化工作。

干杯!