问题 如果类具有特定成员函数,则启用模板函数


我编写了以下模板函数,用于检查仲裁容器是否包含特定元素:

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 启用第一个模板if container_t 不提供 count 成员函数和第二个模板(如果有)。但是我无法弄清楚如何检查特定成员函数(使用C ++ 11)。


5466
2018-04-08 17:06


起源

如果您愿意将boost作为依赖项添加 boost.TTI 有你想要的东西 BOOST_TTI_HAS_MEMBER_FUNCTION 宏 - Simon Gibbons
你完全确定要这么做吗?在标准库中,它们会让您拼出哪个 find 您要使用的表单,以便在进行线性搜索时以及使用更有效的机制时明确清楚。作为旁注如果你真的想要这样做,一定要使用 find 代替 count,这只是避免过早的悲观情绪。 - Mark B
@SimonGibbons:谢谢,它看起来像我需要的东西。但是我现在不想依靠提升。 - Haatschii
@MarkB:据我所知 std::find 根据我们是否有订购的容器,还使用不同的算法,不是吗?至少 en.cppreference.com/w/cpp/algorithm/find 写道,复杂性至多是线性的,即对于合适的容器可以小于线性。如果我错了,请纠正我。 - Haatschii
@Haatschii标准只是说“最后一次比较”,所以假设它对所有容器类型都是线性的。 - Mark B


答案:


C ++ 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 当且仅当 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 是一个继承自的traits类 true_type 当且仅当 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”。他们有自定义扩展来检测成员函数的存在。但那是因为它们远不符合C ++ 11标准。


15
2018-04-08 17:24



VS 2015(至少预览版) 也不支持它 - Anton Savin
有趣的方法。适合我(GCC 4.8)。非常感谢你。 - Haatschii