我想寫一個MyClass
的構造函數,它帶有一個參數,並且我希望只在參數爲pointer
或iterator
(iterator_traits
)時才編譯它。如何實現這一目標?std :: enable_if或SFINAE用於迭代器或指針
6
A
回答
11
遺憾的是,沒有標準的方法來檢測一個班級是否模型Iterator
。最簡單的檢查是*it
和++it
在語法上都是有效的;
template<typename T,
typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())>
MyClass(T);
考慮從24.2.2的Iterator
要求:2:您可以使用標準SFINAE技術做到這一點
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++有conforming extension,因此在非迭代器類型上實例化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);
然而,這將不會爲不暴露自己iterator_category
但已經適應由獨立iterator_traits
專業化工種;在這種情況下,簡單的SFINAE方法更有意義(並且您可以在構造函數中實例化iterator_traits
以確認該類型是類似於迭代器的)。
相關問題
- 1. 返回指針或迭代器
- 2. 迭代器指針或引用 - 錯誤
- 3. 迭代器等價於空指針?
- 4. 迭代指針的std :: vector錯誤
- 5. SFINAE與std :: enable_if和std :: is_default_constructible在libC++中的不完整類型
- 6. 傳遞指針代替迭代器的std ::複製
- 7. 迭代與自動ref或迭代器
- 8. 迭代器載體指針
- 9. 通用迭代器/指針初始化
- 10. 都使用const指針或指針作爲'的std ::關鍵map`
- 11. 迭代器與參考與指針
- 12. 獲取迭代器引用的指向STL容器的指針?
- 13. 解除迭代器到指針
- 14. 迭代指針向量指針
- 15. 共享指針的向量迭代器
- 16. end()迭代器上的指針運算
- 17. &*爲原始指針,迭代器和... std :: nullptr_t
- 18. enumurator或迭代器?
- 19. std ::映射值或指針的操作?
- 20. 如何最好地迭代C數組?用指針或索引?
- 21. C++迭代器,接口和指針
- 22. 迭代器和指針如何相關?
- 23. std :: shared_ptr迭代器
- 24. 指針或索引?
- 25. 指針或引用
- 26. 指向矢量指針Vs的指針迭代器
- 27. 如何迭代指針指針?
- 28. 專門針對可轉換爲指針的迭代器的類
- 29. unordered_map迭代器迭代爲空指針,違反可能性
- 30. for循環或迭代器?
@LucDanton同意,我把SFINAE作爲首選技術。 – ecatmur
偉大的答案,你救了我解釋我們在libstdC++中使用的擴展:)關於該擴展的背景,請參閱http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40497。檢查'iterator_category'的想法歸功於Alisdair Meredith。 –
Mmmh,'* it'的要求是該類型是'std :: iterator_traits :: reference';不是它是一個引用類型(至少對於Iterator而言)。但是你不能使用'std :: iterator_traits'來擔心搞亂SFINAE ......我會讓你修復那個! (你測試的某些表達式的值類別也有問題;例如'++ std :: declval ()',而不是'T'。) –