5

可能重复:
是否可以编写 C++ 模板来检查函数是否存在?

说有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()来区分它们',我该怎么做?

4

4 回答 4

6

替换失败不是错误,或者更简洁地说,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);
}
于 2010-10-19T00:05:00.043 回答
4

从这里窃取,并假设你修复了你的代码,所以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);
}

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

于 2010-10-19T00:01:05.573 回答
0

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

但是,不要那样做。

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

干杯&hth.,

于 2010-10-18T23:58:59.243 回答
0

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

于 2010-10-18T23:58:59.697 回答