Как определить, содержит ли класс определенную функцию-член во время компиляции

Возможный дубликат:
Можно ли написать шаблон 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 ответа

Решение

Кража отсюда, и если вы исправите свой код так 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);
}

Это довольно ужасный дизайн, хотя. Вы должны решить проблему, а не применять патч.

Ошибка замещения не является ошибкой, или, более компактно, 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);
}

Это именно то, для чего наследование. Вы можете легко использовать использование dynamic_cast для этого типа вопросов во время выполнения. Например, вы можете определить абстрактный базовый класс с именем HasGetInt и извлечь из него классы, которым нужна эта функция, а не заново изобретать колесо.

Технически это просто включает в себя несколько шаблонных арканов, которые вы можете найти, прибегая к помощи, например: has_member или т.п. Если бы не код, в коде обнаружения, если бы я написал такой код, я бы просто выдумал производную от рассматриваемого класса и проверил размер члена производного класса.

Однако не делайте этого.

Что еще делать, зависит. Но кажется, что ваши классы соответствуют двум различным "схемам", так сказать, без этих схем, доступных через систему типов (например, кажется, что классы не являются производными от двух базовых классов A и B). Тогда одним из вариантов является введение шаблона признаков, который сообщает оберткам, является ли параметр шаблона T схемой A или B. Специализируйте признаки для каждого соответствующего класса, который отличается от значения по умолчанию. Выберите значение по умолчанию, чтобы минимизировать работу.

Ура & hth.,

Другие вопросы по тегам