Если вы готовы сделать это, вы можете, с помощью метапрограммирования, помочь вам. По сути, вам нужно написать метафункцию шаблона, которая определяет условие, при котором вы хотите вызывать одну или другие функции, и использовать это в предложении enable_if_c
:
template <typename T> inline
typename enable_if_c< has_key_type<T>::value, bool >::type
contains( T const & c, T::key_type const & k ) {...} // associative container
template <typename T> inline
typename enable_if_c< !has_key_type<T>::value, bool >::type
contains( T const & c, T::value_type const & k ) {...} // non-associative container
Шаблон enable_if_c
- это простой общий трюк SFINAE (который вы можете использовать из компилятора C ++ 0x или boost). Он принимает условие и тип, если условие истинно, он сгенерирует внутреннюю typedef для типа аргумента, если его нет, он не определит этот внутренний тип, и его можно использовать снаружи в SFINAE:
template <bool condition, typename T>
struct enable_if_c {}; // by default do not declare inner type
template <typename T>
struct enable_if_c<true,T> { // if true, typedef the argument as inner type
typedef T type;
};
Теперь интересная часть состоит в том, как определить, что тип T
имеет внутренний тип key_type
, и, хотя возможны другие варианты, первое, что приходит на ум:
template <typename T>
class has_key_type {
typedef char _a;
struct _b { _a x[2]; };
template <typename U>
static _a foo( U const &, typename U::key_type* p = 0 );
static _b foo( ... );
static T& generate_ref();
public:
static const bool value = sizeof(foo(generate_ref())) == sizeof(_a);
};
Шаблон has_key_type
будет содержать внутреннюю константу value
, которая равна true
, только если тип, переданный int, содержит внутренний тип T::key_type
. Разрешения не слишком сложны: определите шаблонную функцию, которая не будет работать для всех типов, кроме той, которую вы хотите обнаружить, и предоставьте другую перегрузку с многоточием, так что она будет ловить, если шаблон (имеет более высокий приоритет, чем многоточие) не сможет замена. Затем используйте размер возвращаемых типов, чтобы определить, какая перегрузка была выбрана компилятором. generate_ref
существует для того, чтобы не создавать объект (т. Е. Не навязывать, что T
может быть создан любым конкретным способом).
Общий результат заключается в том, что когда тип T
содержит внутренний тип key_type
, результат has_key_type<T>::value
будет равен true, а enable_if_c
включит первую перегрузку и отключит вторую, поэтому SFINAE будет Отмените вторую перегрузку не из-за того, что аргумент функции типа второй не определен, а из-за условий возвращаемого типа.
Если вы подумаете об этом, вокруг небольшого изменения есть всего лишь куча кода котельной пластины: вместо двух неоднозначных перегрузок функции шаблона, обеспечьте перегрузку шаблона и функцию с меньшим приоритетом (многоточие). Поскольку вы не можете реально использовать эту многоточную функцию для реализации неассоциативной контейнерной версии, она используется для получения логического значения, которое затем добавляется в общие структуры метапрограммирования и шаблон.