2010-10-18 7 views
4

Можно создать дубликат:
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 () или нет во время компиляции ', как мне это сделать?

+2

Возможно, вам стоит изучить виртуальные функции и полиморфизм. – JoshD

+0

Если вы не предполагаете, что у ваших классов-экземпляров, отличных от 'GetInt', у всех есть член 'int', называемый' m'. Я не вижу, как вы можете расширять его без специализаций для каждого. Какова стандартная реализация шаблона вашей функции? –

+2

Вы не можете заставить эту функцию работать, потому что 'GetInt' не const, а' t' есть. – GManNickG

ответ

4

Кража из here, и если вы исправить свой код так GetInt является сопзЬ, получаем:

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

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

+0

ужасно, спасибо за кражу для меня! –

0

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

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

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

Приветствия & НТН.,

0

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

+0

В шаблонном типе он может делать то, что хочет, пока не получит экземпляр с типом. Он мог бы, конечно, перегружать типы функций шаблонов, но я часто считаю, что эти типы решений лучше решаются с помощью модели интерфейса. – rerun

+0

спасибо, ребята, но код устарел, я стараюсь не менять существующий код, но добавляю некоторые полезные функции поверх него, чтобы облегчить жизнь. –

6

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

EDIT: Реальность 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); 
} 
+0

Вы имели в виду t. * Extra() вместо t.GetInt()? Я не вижу, как использовать параметр «extra» – Chubsdad

+1

@Chubsdad: дополнительный параметр присутствует только для того, чтобы вызвать сбой замены, так что перегрузка удаляется из списка совпадений ** до того, как ** экземпляр тела шаблона (в это время компилятор ошибся, пытаясь найти «B :: GetInt»). –

+1

Стоит упомянуть, что decltype не в текущем стандарте ...: - /. Кроме того, вопрос делает его очень понятным: «Я не хочу писать специализацию для каждого типа» - что я бы сделал, чтобы исключить предложение перегрузки. –

 Смежные вопросы

  • Нет связанных вопросов^_^