引数を取るコンストラクターを書きMyClass
たいのですが、引数がapointer
またはiterator
(何かを持っているiterator_traits
)の場合にのみこれをコンパイルしたいと思います。これを達成する方法は?
1 に答える
残念ながら、クラスがモデル化されているかどうかを検出する標準的な方法はありませんIterator
。最も簡単なチェックはそれで*it
あり++it
、両方とも構文的に有効です。これは、標準のSFINAE手法を使用して実行できます。
template<typename T,
typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())>
MyClass(T);
Iterator
24.2.2:2からの要件を考慮して:
template<typename T> typename std::enable_if<
!std::is_void<decltype(*std::declval<T &>())>::value
&& std::is_same<decltype(++std::declval<T &>()),
typename std::add_lvalue_reference<T>::type>::value,
std::true_type>::type has_iterator_requirements_helper(int);
template<typename T> std::false_type has_iterator_requirements_helper(...);
template<typename T> struct has_iterator_requirements:
decltype(has_iterator_requirements_helper<T>(0)) {};
template<typename, bool> struct is_iterator_check: std::false_type {};
template<typename T> struct is_iterator_check<T, true>: std::true_type {
typedef typename std::iterator_traits<T>::difference_type difference_type;
typedef typename std::iterator_traits<T>::value_type value_type;
typedef typename std::iterator_traits<T>::iterator_category iterator_category;
typedef typename std::iterator_traits<T>::reference reference;
typedef typename std::iterator_traits<T>::pointer pointer;
static_assert(std::is_same<decltype(*std::declval<T &>()), reference>::value
|| std::is_void<reference>::value, "*r must be of type reference");
};
template<typename T> struct is_iterator: is_iterator_check<T,
(std::is_pointer<T>::value
&& !std::is_void<typename std::remove_pointer<T>::type>::value
&& !std::is_function<typename std::remove_pointer<T>::type>::value
) || (std::is_copy_constructible<T>::value
&& std::is_copy_assignable<T>::value
&& std::is_nothrow_destructible<T>::value
// TODO: check lvalues are swappable
&& has_iterator_requirements<T>::value
)> {};
使用しようとする際の問題iterator_traits
は、それがすべてのタイプに対して定義されたテンプレートであり、そのインスタンス化が非SFINAEコンテキストで失敗することです(SFINAEは直接置換の失敗にのみ適用されることを思い出してください)。libstdc ++には準拠する拡張機能iterator_traits
があり、非イテレータ型でインスタンス化すると空の型が生成されます。iterator_category
次のタイプの存在を確認することで、同様のトリックを実行できます。
template<typename T> std::true_type has_iterator_category_helper(
T::iterator_category *);
template<typename T> std::false_type has_iterator_category_helper(...);
template<typename T> struct has_iterator_category<T>:
decltype(has_iterator_category_helper<T>(0)) { };
template<typename T> struct is_iterator: std::integral_constant<bool,
std::is_pointer<T>::value || has_iterator_category<T>::value> {};
template<typename T, typename = std::enable_if<is_iterator<T>::value>>
MyClass(T);
ただし、これは、それ自体は公開されていないが、別の専門分野によって適応されているタイプでは機能しません。その場合、単純なSFINAEメソッドの方が理にかなっています(コンストラクター内でインスタンス化して、型がイテレーターのようなものであることを確認できます)。iterator_category
iterator_traits
iterator_traits