C++14 SFINAE 解引用迭代器 原問題:編寫函數f(r),若r為迭代器,則返回f(*r),否則返回r。 摘要: 問題: 什麼是迭代器? 迭代器是c++中的一個概念,若類型It滿足以下條件,則It為迭代器類型 可拷貝構造(CopyConstructible) 可拷貝賦值(CopyAssigna ...
C++14 SFINAE 解引用迭代器
原問題:編寫函數f(r),若r為迭代器,則返回f(*r),否則返回r。
摘要:
問題:
-
什麼是迭代器?
-
迭代器是c++中的一個概念,若類型It滿足以下條件,則It為迭代器類型
-
可拷貝構造(CopyConstructible)
-
可拷貝賦值(CopyAssignable)
-
可析構(Destructibale)
-
左值It對象可交換(Swappable)
-
std::iterator_traits<It>含如下類型成員:value_type, difference_type, reference, pointer和iterator_category
-
對於It的左值r,如下表達式合法且具有指定含義:
-
*r 返回值類型:unspecified 前置條件:r可解引用
-
++r 返回值類型: It & 前置條件:r可自增
-
-
-
在後續實現中,將放寬迭代器的要求:對左值r,設若*r合法,則r有迭代器類型。意即:
編寫函數f(r),若左值r可被解引用,返回f(*r),否則返回r。
問題分析:
f的返回值類型需要隨實際參數的變化而改變。例如:若實際參數類型為int *,則返回值類型為int, 若實際參數類型為int **,則返回值類型為int *。不同形參,不同返回值的同名函數聲明可通過重載來實現。
由於迭代器有無限多種,也無法預知用戶代碼會傳遞哪些類型的迭代器,手動添加這些重載是不現實的。需要藉助編譯器來根據調用自動“生成”(模板實例化)這些函數重載。
結合以上兩點,f的聲明應為含一個模板類型參數的函數模板。在編譯時,由編譯器根據各調用的實際參數,來生成並調用實現對應功能的函數。
-
若類型T的左值可被解引用,則實例化函數模板(1)
template <class T> auto f(T r) { return f(*r); }
- 否則實例化函數模板(2)
template <class T> auto f(T r) { return r; }
問題:
-
如何使得兩個模板“互斥”。
- 方案1:通過SFINAE。當表達式e(檢驗r是否可被解引用)為真時,啟用函數模板1。否則啟用函數模板2
- 方案2:通過SFINAE。當表達式e(檢驗r是否可被解引用)為真時,啟用函數模板1。而函數模板2總是可用,但其重載匹配的優先順序低於由函數模板1(模板1的調用為Exact match,模板2的調用需要Conversion)
實現(方案2):
1 #include <cassert> 2 3 #include <type_traits> 4 #include <utility> 5 6 template <class T> 7 auto f(T x, ...) { 8 return x; 9 } 10 11 template <class T, class = decltype(*std::declval<T>())> 12 auto f(T x, int) { 13 return f(*x, 0); 14 } 15 16 int main() { 17 int x = 3, *p = &x; 18 assert(f(&p, 0)==3); 19 20 return 0; 21 }
正文:
從略。
FAQ:
參考資料:
http://en.cppreference.com/w/cpp/concept/Iterator
http://en.cppreference.com/w/cpp/language/overload_resolution (return type deduction)
http://en.cppreference.com/w/cpp/utility/declval
http://en.cppreference.com/w/cpp/language/overload_resolution
http://en.cppreference.com/w/cpp/language/template_argument_deduction