我写了类似于类的特性,如果给定的类型是"可迭代的话,可以用它来测试#34;。对于数组(对于T[N]
而不对于T[]
)以及具有begin
和end
方法的类返回看起来像迭代器的类,情况都是如此。我想知道它是否可以比我做得更简洁/更简单?
特别是impl
命名空间中的内容看起来有点迂回/ hacky。这一切对我来说都有点难看。有关使用此示例并且可以使用g ++和clang ++编译的示例,请参阅:https://gist.github.com/panzi/869728c9879dcd4fffa8
template<typename T>
struct is_iterator {
private:
template<typename I> static constexpr auto test(void*)
-> decltype(
*std::declval<const I>(),
std::declval<const I>() == std::declval<const I>(),
std::declval<const I>() != std::declval<const I>(),
++ (*std::declval<I*>()),
(*std::declval<I*>()) ++,
std::true_type()) { return std::true_type(); }
template<typename I> static constexpr std::false_type test(...) { return std::false_type(); }
public:
static constexpr const bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
};
namespace impl {
// implementation details
template<typename T>
struct has_iterable_methods {
private:
template<typename C> static constexpr auto test(void*)
-> decltype(
std::declval<C>().begin(),
std::declval<C>().end(),
std::true_type()) { return std::true_type(); }
template<typename C> static constexpr std::false_type test(...) { return std::false_type(); }
public:
static constexpr const bool value = std::is_same<decltype(test<T>(0)), std::true_type>::value;
};
template<typename T, bool HasIterableMethods>
struct returns_iterators : public std::false_type {};
template<typename T>
struct returns_iterators<T, true> {
typedef decltype(std::declval<T>().begin()) begin_type;
typedef decltype(std::declval<T>().end()) end_type;
static constexpr const bool value =
std::is_same<begin_type, end_type>::value &&
is_iterator<begin_type>::value;
};
}
template<typename T>
struct is_iterable : public std::integral_constant<
bool,
impl::returns_iterators<
typename std::remove_const<T>::type,
impl::has_iterable_methods<typename std::remove_const<T>::type>::value>::value> {};
template<typename T, std::size_t N>
struct is_iterable<T[N]> : public std::true_type {};
template<typename T>
struct is_iterable<T*> : public std::false_type {};
答案 0 :(得分:6)
首先,在begin
可见的上下文中,对std::begin
进行简单的参数依赖查找的一些样板:
#include <utility>
#include <iterator>
namespace adl_details {
using std::begin; using std::end;
template<class R>
decltype(begin(std::declval<R>())) adl_begin(R&&r){
return begin(std::forward<R>(r));
}
template<class R>
decltype(end(std::declval<R>())) adl_end(R&&r){
return end(std::forward<R>(r));
}
}
using adl_details::adl_begin;
using adl_details::adl_end;
这需要合理地模拟基于范围的for(:)
循环如何找到它们的开始/结束迭代器。通过这样打包,我们减少了下面的样板。
接下来,一些C ++ 1y样式实用程序别名:
template<class>struct sink {using type=void;};
template<class X>using sink_t=typename sink<X>::type;
template<bool b, class T=void>using enable_if_t=typename std::enable_if<b,T>::type;
sink_t
采用任何类型,并将其删除,将其替换为void
。
enable_if_t
删除了下面恼人的typename
垃圾邮件。
在一个工业强度库中,我们将它放在detail
中,并且有一个分派给它的1类型参数版本。但我不在乎:
template<class I,class=void> struct is_iterator:std::false_type{};
template<> struct is_iterator<void*,void>:std::false_type{};
template<> struct is_iterator<void const*,void>:std::false_type{};
template<> struct is_iterator<void volatile*,void>:std::false_type{};
template<> struct is_iterator<void const volatile*,void>:std::false_type{};
template<class I>struct is_iterator<I,
sink_t< typename std::iterator_traits<I>::value_type >
>:std::true_type{};
is_iterator
不会对iterator_traits
I
进行大量审核。但这已经足够了。
template<class R>
using begin_t=decltype(adl_begin(std::declval<R&>()));
template<class R>
using end_t=decltype(adl_end(std::declval<R&>()));
这两种类型的别名使下面的内容不那么烦人。
再次,在工业强度库中,将2-arg-with - void
放入details
:
template<class R,class=void> struct has_iterator:std::false_type{};
template<class R>
struct has_iterator<
R,
enable_if_t<
is_iterator<begin_t<R>>::value
&& is_iterator<end_t<R>>::value
// && std::is_same<begin_t<R>,end_t<R>>::value
>
>:std::true_type{};
请注意上面enable_if_t
中注释掉的行。我把它留下来允许非对称迭代工作,其中end
是一种具有不同operator==
重载的类型。对于C ++ 17正在考虑这样的问题:它允许在以null结尾的字符串上实现真正有效的算法(例如)。
最后,最终输出:
template<class R>using iterator_t=enable_if_t<has_iterator<R>::type, begin_t<R>>;
如果它有一个,那么它将计算可迭代范围R
的迭代器。
有些情况下这不起作用,但它们是病态的。