Подтвердить что ты не робот

Включить функцию шаблона, если класс имеет определенную функцию-член

Я написал следующую функцию шаблона, которая проверяет, содержит ли суровый контейнер определенный элемент:

template<template<class, class...> class container_t, class item_t, class... rest_t>
bool contains(const container_t<item_t, rest_t...> &_container, const item_t &_item) {
    for(const item_t &otherItem : _container) {
        if(otherItem == _item) { return true; }
    }
    return false;
}

Это хорошо работает для большинства контейнеров. Однако для всех видов множеств (и карт) оно является оптимальным, поскольку мы могли бы использовать:

template<template<class, class...> class set_t, class item_t, class... rest_t>
bool contains(const set_t<item_t, rest_t...> &_set, const item_t &_item) {
    return _set.count(_item) > 0;
}

Очевидно, мы не можем использовать оба шаблона одновременно из-за неоднозначности. Теперь я ищу способ использовать std::enable_if, чтобы включить первый шаблон, если container_t не предоставляет функцию-член count и второй шаблон, если это так. Однако я не могу понять, как проверить определенную функцию-член (используя С++ 11).

4b9b3361

Ответ 1

Функция С++ 14, переопределенная:

template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;

Библиотека мини-метапрограммирования:

template<class...>struct types{using type=types;};
namespace details {
  template<template<class...>class Z, class types, class=void>
  struct can_apply : std::false_type {};
  template<template<class...>class Z, class...Ts>
  struct can_apply< Z, types<Ts...>, void_t< Z<Ts...> > >:
    std::true_type
  {};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,types<Ts...>>;

can_apply< some_template, args... > наследует от true_type iff some_template<args...> - допустимое выражение (в непосредственном контексте).

Теперь для вашей проблемы:

template<class T, class I>
using dot_count_type = decltype( std::declval<T>().count(std::declval<I>()) );

template<class T, class I>
using has_dot_count = can_apply<dot_count_type, T, I>;

и has_dot_count - класс признаков, который наследует от true_type iff T.count(I) - допустимое выражение.

namespace details {
  template<class C, class I>
  bool contains(std::false_type, C const& c, I const& i) {
    for(auto&& x:c) {
      if(x == i) { return true; }
    }
    return false;
  }
  template<class C, class I>
  bool contains(std::true_type, C const& c, I const& i) {
    return c.count(i) != 0;
  }
}
template<class C, class I>
bool contains( C const& c, I const& i ) {
  return details::contains( has_dot_count<C const&,I const&>{}, c, i );
}

который использует диспетчеризацию меток вместо SFINAE.

Использование find кажется лучшей идеей, чем .count как сторонняя. Фактически, в одном случае вы должны использовать .find другой find. В обоих случаях вы должны использовать using std::end; auto e = end(c);.

Как в стороне, MSVC 2013 (я не знаю о 2015) не обрабатывает этот вид SFINAE, использованный выше. Они называют это "выражением SFINAE". У них есть пользовательские расширения для обнаружения существования функции-члена. Но это потому, что они далеки от совместимого с С++ 11.