C++ 函數模板的返回類型如何確定?
函數模板
#include <iostream> // 多個參數的函數木板 template<typename T1, typename T2> T2 max(T1 a, T2 b) { using namespace std; cout << "調用的自定義模板函數...... " << endl; return b < a ? a : b; } // 顯式指定模板參數的類型 template<typename T> T max1(T a, T b) { using namespace std; cout << "顯式指定模板參數...... " << endl; return b < a ? a : b; } int main() { using namespace std; double a = ::max(2, 3.2); cout << "max(2, 3.2) = " << a << endl; double b = ::max1<double>(2, 4); cout << "max1<double>(2, 4) = " << b << endl; return 0; }
函數模板有兩種不同類型的參數:
模板參數:在尖括號中聲明的
template<typename T>
調用參數:函數模板名稱的括號中聲明的
T max(T a, T b)
如果使用了多個模板類型參數的話,返回值有時候是個問題,對此有三種方法:
- 為返回值單獨引入一個模板類型參數
- 讓編譯器找到返回類型
- 聲明一個其他類型的共同類型
返回值的模板參數
函數模板可以自動檢測模板參數類型,因此我們可以不同顯式指定,當然顯式指定也可以的。
比如:
#include <iostream> // 顯式指定模板參數的類型 template<typename T> T max1(T a, T b) { using namespace std; cout << "顯式指定模板參數...... " << endl; return b < a ? a : b; } int main() { using namespace std; double b = ::max1<double>(2, 4); cout << "max1<double>(2, 4) = " << b << endl; return 0; }
在進行 ::max1<double>(2, 4); 調用的時候,就是顯式指定了模板參數。
現在來一個為返回值引入第三個模板參數:RT max(T1 a, T2 b);
#include <iostream> // 多個參數的函數木板 template<typename T1, typename T2, typename RT> RT max(T1 a, T2 b) { using namespace std; cout << "調用的自定義模板函數...... " << endl; return b < a ? a : b; } int main() { using namespace std; double a = ::max(2, 3.2); cout << "max(2, 3.2) = " << a << endl; return 0; }
然後編譯的時候就報錯了:
報錯信息提示:參數 deduction(推理)失敗了
這裏引出了自動推理的規則:
如果模板參數和調用參數的類型沒有什麽關系,那麽在調用的時候 參數模板的所有類型是不能完全確定的,你必須顯式指定模板參數。
因此顯式指定以下就可以了:
#include <iostream> // 多個參數的函數木板 template<typename T1, typename T2, typename RT> RT max(T1 a, T2 b) { using namespace std; cout << "調用的自定義模板函數...... " << endl; return b < a ? a : b; } int main() { using namespace std; // 模板參數和調用參數不能完全匹配的時候需要顯式指定 double a = ::max<int, double, double>(2, 3.2); cout << "max(2, 3.2) = " << a << endl; return 0; }
編譯通過,運行成功。
但是指定所有的模板類型又有點啰嗦,這裏又來了一條規則:
你必須顯式指定直到最後一個不能確定的所有模板類型。
比如上面例子中,RT 的類型是不能通過參數類型推理來確定的,所以導致 T1 和 T2 的類型都要顯式指定。
因此這裏來了一個騷操作,就是把不能指定的模板類型都放在模板參數列表的最前面,那麽後面可以確定的模板參數就可以不用顯式指定了。
#include <iostream> // 多個參數的函數木板 template<typename RT, typename T1, typename T2> RT max(T1 a, T2 b) { using namespace std; cout << "調用的自定義模板函數...... " << endl; return b < a ? a : b; } int main() { using namespace std; // 這個 double 專門為了 RT 而指定的, T1 和 T2 都可以通過調用參數推導出來 double a = ::max<double>(2, 3.2); cout << "max(2, 3.2) = " << a << endl; return 0; }
我只想說 C++ 是真的騷。
編譯器推理返回類型
如果返回類型依賴於模板參數,那麽最簡單的方法就是讓編譯器去推導類型。
C++ 14 中已經可以不用指定返回類型了,但是你還是要用 auto 來聲明返回類型。
如下面的例子:
#include <iostream> // 多個參數的函數木板 template<typename T1, typename T2> auto max(T1 a, T2 b) { using namespace std; cout << "調用的自定義模板函數...... " << endl; return b < a ? a : b; } int main() { using namespace std; // 這個 double 專門為了 RT 而指定的, T1 和 T2 都可以通過調用參數推導出來 auto a = ::max(2, 3.2); auto b = ::max(5, 1.2); cout << "max(2, 3.2) = " << a << endl; cout << "max(5, 1.2) = " << b << endl; return 0; }
大爺的,我又要去學下 auto 是怎麽用的。
使用了 auto 就可以不用 trailing of return type(返回類型後置),但是實際的返回類型還是要通過函數體中的返回語句來推導。
當然了,根據函數體來反推返回值類型要是可行的。
decltype
decay
看到這裏有點懵逼。
公共類型作為返回類型
C++ 11 中,標準庫提供了一種對多個類型生成共同類型的方法。
例如:
#include <iostream> // 引入這個玩意兒生成共同類型 #include <type_traits> // 騷裏騷氣啊 template<typename T1, typename T2> std::common_type_t<T1, T2> max(T1 a, T2 b) { using namespace std; cout << "調用的自定義模板函數...... " << endl; return b < a ? a : b; } int main() { using namespace std; // 這個 double 專門為了 RT 而指定的, T1 和 T2 都可以通過調用參數推導出來 auto a = ::max(2, 3.2); auto b = ::max(5, 1.2); cout << "max(2, 3.2) = " << a << endl; cout << "max(5, 1.2) = " << b << endl; return 0; }
std::common_type 是一種 type trait,能夠為返回類型生成一種結構。
typename std::common_type<T1, T2>::type // since C++ 11
而在 C++ 14 中
std::cmmon_type_t<T1, T2> // C++ 14 中的寫法
服!氣!
C++ 函數模板的返回類型如何確定?