匹配可迭代类型(具有begin()/ end()的数组和类) [英] Match iterable types (arrays and classes with begin()/end())
问题描述
我写了类型traits像类,可以使用测试,如果给定的类型是可迭代。这适用于数组( T [N]
,而不是 T []
), begin
和一个结束
方法返回的东西看起来像迭代器。
特别是 impl
中的东西,命名空间看起来有点迂回/ hacky。这一切看起来有点丑。有关使用此示例并可以使用g ++和clang ++编译的示例,请参见: https://gist.github.com / panzi / 869728c9879dcd4fffa8
模板< typename T&
struct is_iterator {
private:
template< typename I>静态constexpr自动测试(void *)
- > decltype(
* std :: declval< const I>(),
std :: declval< const I>()== std :: declval< const I> :: declval< const I>()!= std :: declval< const I>(),
++(* std :: declval< I *>()),
:: 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>
};
namespace impl {
//实现细节
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>
};
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 {};
begin
在 std :: begin
可见的上下文中:
#include< utility>
#include namespace adl_details {
using std :: begin;使用std :: end;
template< class R>
decltype(begin(std :: declval< R>()))adl_begin(R& r){
return begin(std :: forward&
}
template< class R>
decltype(end(std :: declval< R>()))adl_end(R& r){
return end(std :: forward&
}
}
使用adl_details :: adl_begin;
using adl_details :: adl_end;
这需要合理模拟基于范围的 / code> loops找到它们的begin / end迭代器。
接下来,一些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&
sink_t
可以使用任何类型,将其替换为 void
。
enable_if_t
typename
垃圾邮件。
在工业实力库中, detail ,并且有一个1类型参数版本。但我不在乎:
模板< class I,class = void> struct is_iterator:std :: false_type {};
模板<> struct is_iterator< void *,void>:std :: false_type {};
模板<> struct is_iterator< void const *,void>:std :: false_type {};
模板<> struct is_iterator< void volatile *,void>:std :: false_type {};
模板<> 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
I
的 iterator_traits
。
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与 void
放入细节
:
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
是一个类型具有不同的运算符==
超载。这是为C ++ 17考虑的:它允许真正有效的算法对空终止的字符串(例如)。
最后, p>
template< class R> using iterator_t = enable_if_t< has_iterator< R> :: type,begin_t< R>
其计算结果为可迭代范围的迭代器 R $ c $
p> 实例
I wrote type traits like classes that can be used test if a given type is "iterable". This is true for arrays (for T[N]
, not for T[]
) and for classes that have a begin
and an end
method that return things that look like iterators. I wonder if it can be done more concise/simpler than I did it?
Especially the stuff in the impl
namespace look a bit roundabout/hacky. It all looks a bit ugly to me. For an example that uses this and can be compiled with g++ and clang++ see: 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 {};
First, some boilerplate to do easy argument dependent lookup of begin
in a context where std::begin
is visible:
#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;
This is required to reasonably emulate how range-based for(:)
loops find their begin/end iterators. By packaging it up like this, we reduce boilerplate below.
Next, some C++1y style utility aliases:
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
takes any type, and throws it away replacing it with void
.
enable_if_t
removes annoying typename
spam below.
In an industrial strength library, we'd put this in detail
s, and have a 1-type-argument version that dispatches to it. But I don't care:
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
doesn't do heavy auditing of the iterator_traits
of I
. But it is enough.
template<class R>
using begin_t=decltype(adl_begin(std::declval<R&>()));
template<class R>
using end_t=decltype(adl_end(std::declval<R&>()));
These two type aliases make the stuff below less annoying.
Again, in industrial strength libraries, put 2-arg-with-void
into 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{};
Note the commented out line in the enable_if_t
above. I left that out to allow asymmetric iteration to work, where the end
is a type that has a different operator==
overload. Such is being considered for C++17: it allows really, really efficient algorithms on null-terminated strings (for example).
Finally, the final output:
template<class R>using iterator_t=enable_if_t<has_iterator<R>::type, begin_t<R>>;
which evaluates to the iterator of the iterable range R
iff it has one.
There are cases where this won't work, but they are pathological.
这篇关于匹配可迭代类型(具有begin()/ end()的数组和类)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!