Простой способ с decltype
- это
template<typename C, typename V>
auto Contains(const C& c, const V& value)
-> decltype(std::find(c.cbegin(), c.cend(), value) != c.cend())
{
return std::find(c.cbegin(), c.cend(), value) != c.cend();
}
template <typename C, typename Key>
auto Contains(const C& c, const Key& key)
-> decltype(c.find(key) != c.end())
{
return c.find(key) != c.end();
}
, но тогда, когда обе функции возможны, вы получите неоднозначный вызов.
Так что просто добавьте дополнительный параметр, чтобы расставить приоритеты перегрузки:
struct low_priority {};
struct high_priority : low_priority {};
template<typename C, typename V>
auto ContainsImpl(low_priority, const C& c, const V& value)
-> decltype(std::find(c.cbegin(), c.cend(), value) != c.cend())
{
return std::find(c.cbegin(), c.cend(), value) != c.cend();
}
template <typename C, typename Key>
auto ContainsImpl(high_priority, const C& c, const Key& key)
-> decltype(c.find(key) != c.end())
{
return c.find(key) != c.end();
}
template <typename C, typename T>
auto Contains(const C& c, const T& t)
-> decltype(ContainsImpl(high_priority{}, c, t))
{
return ContainsImpl(high_priority{}, c, t);
}
Теперь о вашей версии у вас есть несколько проблем
Последняя:
// Expected Fallback: uses std::find() to do the lookup if no type-specific T::find(value) exists
template<typename T>
bool Contains(const T&, const typename T::value_type&);
// Expected Preferred: use T::find() to do the lookup if possible
template<typename T>
typename std::enable_if<Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
SFINAE позволяет отбрасывать перегрузки, но не расставлять приоритеты.Вы должны использовать приоритет, как показано выше, или создать исключительный набор перегрузки:
template<typename T>
typename std::enable_if<!Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
template<typename T>
typename std::enable_if<Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
В дополнение к этому, как упомянуто в комментарии, map
семейство будет использовать key_type
, а не value_type
.
Тогда ваш код обнаружения содержит ошибки,
// Вот как примеры показывают, что это делается для функции 0-arg // шаблон static YesType & Test (decltype (& C:: find));
Нет, это обнаруживает, если C
имеет метод find
(без перегрузки).
template <typename C> static YesType&
Test(decltype(std::declval<C>().find(std::declval<const C::value_type&>())));
Здесь,вы используете SFINAE, но конечным типом будет (const_
) iterator
, и Test<C>(0)
не будет принимать эту перегрузку (если только итератор не может быть собран из 0
, что не является обычным случаем).Возможно добавление дополнительных *
, тогда у вас есть указатель на итератор, который можно инициализировать с помощью 0
.
В противном случае вы можете использовать код, указанный в предоставленной ссылке:
namespace detail{
template<class T, typename ... Args>
static auto test_find(int)
-> sfinae_true<decltype(std::declval<T>().find(std::declval<const Arg&>()...))>;
template<class, class ...>
static auto test_find(long) -> std::false_type;
} // detail::
template<class C, typename ... Args>
struct has_find : decltype(detail::test_find<T, Args...>(0)){};
// int has higher priority than long for overload resolution
а затем используйте свои черты с std::enable_if
has_find<Container, Key>::value
.