最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++ 函數(shù)模板完全指南(最新整理)

 更新時(shí)間:2026年03月30日 09:26:23   作者:簡(jiǎn)單~  
文章詳細(xì)解釋了C++中的函數(shù)模板,通過(guò)示例代碼展示了函數(shù)模板的應(yīng)用,文章還討論了模板函數(shù)與普通函數(shù)的優(yōu)缺點(diǎn),以及使用模板特化和重載的時(shí)機(jī),感興趣的朋友跟隨小編一起看看吧

??從泛型思想、模板特化到排序算法實(shí)戰(zhàn) —— 基于現(xiàn)代C++深度剖析, 函數(shù)模板讓算法脫離數(shù)據(jù)類(lèi)型 —— 真正實(shí)現(xiàn) “Write once, use for any type”.

? 一、為什么需要函數(shù)模板?

在C++中,如果需要實(shí)現(xiàn)功能相同但數(shù)據(jù)類(lèi)型不同的函數(shù)(例如交換兩個(gè)整數(shù)、兩個(gè)浮點(diǎn)數(shù),或者對(duì)不同類(lèi)型數(shù)組排序),傳統(tǒng)做法需要為每種類(lèi)型重載函數(shù),導(dǎo)致大量重復(fù)代碼。函數(shù)模板允許編寫(xiě)通用的函數(shù),編譯器會(huì)根據(jù)實(shí)參類(lèi)型自動(dòng)生成對(duì)應(yīng)的函數(shù)版本,實(shí)現(xiàn)了“一次編寫(xiě),多類(lèi)型使用”,極大提高代碼復(fù)用性與可維護(hù)性。

?? 核心思想:將數(shù)據(jù)類(lèi)型參數(shù)化,使用 template <typename T> 或 template <class T> 定義類(lèi)型占位符。

?? 二、函數(shù)模板定義與基本語(yǔ)法

語(yǔ)法格式:

template <typename T>
T 函數(shù)名(T 參數(shù)1, T 參數(shù)2) {
    // 函數(shù)體
}

基于示例代碼中的交換模板和加法模板:

// 交換模板
template<typename T>
void swap_T(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}
// 加法模板
template<class T>
T add_T(T a, T b) {
    return a + b;
}

?? 調(diào)用方式

  • 自動(dòng)類(lèi)型推導(dǎo):swap_T(a, b); 編譯器根據(jù)實(shí)參推斷 T 的類(lèi)型。
  • 顯式指定類(lèi)型:swap_T<int>(a, b); 或 add_T<int>(a, d); 適用于需要類(lèi)型轉(zhuǎn)換或避免二義性。

?? 關(guān)鍵限制:自動(dòng)推導(dǎo)時(shí),所有模板參數(shù)必須保持一致的類(lèi)型。例如 swap_T(a, d)(一個(gè)int一個(gè)double)會(huì)導(dǎo)致推導(dǎo)失敗。但顯式指定類(lèi)型時(shí)允許隱式轉(zhuǎn)換(值傳遞場(chǎng)景)。

?? 三、無(wú)參數(shù)的模板函數(shù) & 值初始化安全性

當(dāng)模板參數(shù)無(wú)法從函數(shù)參數(shù)推導(dǎo)時(shí),必須顯式指定類(lèi)型。示例中 test_T<int>() 使用了 C++11 的 統(tǒng)一初始化 {} ,防止未初始化變量和窄化轉(zhuǎn)換。

template<typename T>
void test_T() {
    T temp{};        // 值初始化,int為0,double為0.0,自定義類(lèi)調(diào)用默認(rèn)構(gòu)造
    T temp1{0};      // 安全
    // T temp2{0.5}; // 編譯錯(cuò)誤:窄化轉(zhuǎn)換(若T為int)
    temp = 1.5;      // 隱式轉(zhuǎn)換,int會(huì)截?cái)?
}

? 最佳實(shí)踐:在模板中盡量使用 T var{} 保證變量總是被初始化,提高泛型代碼的魯棒性。

?? 四、函數(shù)模板特化 (Explicit Specialization)

通用模板無(wú)法滿(mǎn)足某些特殊類(lèi)型的需求時(shí)(例如自定義類(lèi) MyPoint 無(wú)法直接用 << 輸出,或者需要特殊格式),可以為特定類(lèi)型提供特化版本。編譯器會(huì)優(yōu)先匹配特化版本。

// 通用模板
template<typename T>
void printArr(T arr[], int len) { ... }
// 針對(duì) int 類(lèi)型的特化 (分隔符變?yōu)?" / ")
template<>
void printArr<int>(int arr[], int len) {
    for (int i = 0; i < len; i++) cout << arr[i] << " / ";
}
// 針對(duì) MyPoint 類(lèi)型的特化 (優(yōu)雅輸出坐標(biāo))
template<>
void printArr(MyPoint arr[], int len) {
    for (int i = 0; i < len; i++) {
        cout << "(" << arr[i].getX() << "," << arr[i].getY() << ")";
    }
}

調(diào)用時(shí),若傳入 MyPoint 數(shù)組,自動(dòng)選擇特化版本,實(shí)現(xiàn)定制行為。

?? 五、普通函數(shù) vs 函數(shù)模板 vs 模板特化 —— 調(diào)用優(yōu)先級(jí)

C++ 編譯器選擇調(diào)用哪個(gè)函數(shù)有一套明確的規(guī)則(優(yōu)先級(jí)從高到低):

優(yōu)先級(jí)候選類(lèi)型示例
1?? 最高完全匹配的普通函數(shù)void printArr(int arr[], int len)
2??模板特化template<> void printArr<int>(int arr[], int len)
3??函數(shù)模板實(shí)例化template<typename T> void printArr(T arr[], int len)
4?? 最低經(jīng)過(guò)隱式轉(zhuǎn)換的普通函數(shù)例如 printArr(charArr, len) 不存在時(shí)可能會(huì)嘗試轉(zhuǎn)換
// 普通函數(shù) (打印int數(shù)組使用逗號(hào)分隔)
void printArr(int arr[], int len) { ... }
// 調(diào)用時(shí):
printArr(int_arr, num);    // 匹配普通函數(shù) (優(yōu)先級(jí)最高)
printArr<>(int_arr, num);  // 空模板列表強(qiáng)制調(diào)用函數(shù)模板版本 (跳過(guò)普通函數(shù))

?? 注意:普通函數(shù)和函數(shù)模板最好不要提供完全相同的重載,容易引發(fā)二義性。若希望總是使用模板,使用空尖括號(hào) <> 強(qiáng)制指定。

?? 六、類(lèi)型轉(zhuǎn)換 & 引用的特殊規(guī)則

基于代碼中的 test1 可總結(jié):

  • 值傳遞:允許隱式類(lèi)型轉(zhuǎn)換(如 addInt(a, c) 將 char 提升為 int)。
  • 引用傳遞:swapInt(a, c) 錯(cuò)誤!引用不能綁定到不同類(lèi)型轉(zhuǎn)換后的臨時(shí)變量。
    但是 const 引用 可以綁定到臨時(shí)量(安全,只讀)。
  • 模板引用參數(shù):同樣不支持隱式轉(zhuǎn)換,保持類(lèi)型嚴(yán)格一致。例如 swap_T(a, d) 編譯失敗。

?? 解決方案:需要處理不同類(lèi)型時(shí),可顯式指定模板參數(shù),或利用重載/函數(shù)重載決議。

?? 七、算法模板實(shí)戰(zhàn):選擇排序 & 冒泡排序 (泛型排序)

以下兩個(gè)排序模板可以處理 任意支持比較運(yùn)算符 < 的類(lèi)型數(shù)組(int, double, char, 甚至自定義類(lèi)若重載了運(yùn)算符)。

?? 1.選擇排序模板 (不穩(wěn)定,每輪選擇最大/最小交換)

template<typename T>
void mySelectionSort_T(T arr[], int len) {
    for (int i = 0; i < len-1; i++) {
        int max = i;
        for (int j = i+1; j < len; j++)
            if (arr[max] < arr[j]) max = j;   // 降序
        if (max != i) swap_T(arr[max], arr[i]);
    }
}

?? 2.冒泡排序模板 (穩(wěn)定,提前退出優(yōu)化)

template<typename T>
void myBubbleSort_T(T arr[], int len) {
    for (int i = 0; i < len-1; i++) {
        bool swapped = false;
        for (int j = 0; j < len-1-i; j++) {
            if (arr[j] < arr[j+1]) {  // 降序排列
                swap_T(arr[j], arr[j+1]);
                swapped = true;
            }
        }
        if (!swapped) break;
    }
}

在 test2 中對(duì) "hello world" 字符數(shù)組進(jìn)行降序排序,test3/test4 演示整型數(shù)組的排序過(guò)程,充分體現(xiàn)了模板對(duì) char 和 int 的通用性。

?? 八、完整測(cè)試模塊說(shuō)明

程序中的 test1~test5 覆蓋了模板的所有關(guān)鍵特性,下面逐一說(shuō)明:

  • test1():演示普通函數(shù) vs 模板交換、加法、自動(dòng)推導(dǎo)失敗案例、顯式指定類(lèi)型 + 隱式轉(zhuǎn)換、無(wú)參模板必須顯式實(shí)例化。
  • test2():字符數(shù)組選擇排序 + printArr 重載測(cè)試,展示默認(rèn)模板和自定義連接符模板的重載。
  • test3():整型數(shù)組選擇排序,并演示 printArr<>(int_arr, num) 強(qiáng)制調(diào)用模板版本。
  • test4():冒泡排序模板測(cè)試,驗(yàn)證提前退出優(yōu)化。
  • test5():函數(shù)調(diào)用優(yōu)先級(jí)測(cè)試 —— 普通函數(shù) printArr(int[],len) 被優(yōu)先調(diào)用,特化版本處理 MyPoint,以及空模板參數(shù)強(qiáng)制使用模板。

?? 注意:test5 中通過(guò) MyPoint point_arr[] 調(diào)用 printArr 會(huì)自動(dòng)匹配模板特化版本,輸出格式化為 (x,y)。

?? 九、易錯(cuò)點(diǎn) & 最佳實(shí)踐總結(jié)

?? 模板定義通常放在頭文件中,因?yàn)榫幾g器需要在使用時(shí)看到完整定義才能實(shí)例化。
?? 函數(shù)模板不支持默認(rèn)模板參數(shù)(C++11 后支持,但函數(shù)模板常用默認(rèn)類(lèi)型參數(shù)較少見(jiàn),類(lèi)模板更常見(jiàn))。
?? 模板特化時(shí) template<> 必須前置,且參數(shù)列表必須匹配原模板。
?? 避免過(guò)度特化,優(yōu)先使用重載或 if constexpr (C++17)。
?? 如果希望模板只適用于具有某種特性的類(lèi)型,可以結(jié)合 SFINAE 或 C++20 Concept。

?? 十、關(guān)鍵代碼與模板

// 1. 泛型交換
template<typename T> void mySwap(T& a, T& b) { T t = a; a = b; b = t; }
// 2. 泛型打印數(shù)組(通用)
template<typename T>
void printArray(const T arr[], size_t len) {
    for (size_t i = 0; i < len; ++i) cout << arr[i] << " ";
}
// 3. 針對(duì)int的特化
template<> void printArray(const int arr[], size_t len) {
    for (size_t i = 0; i < len; ++i) cout << arr[i] << " | ";
}
// 4. 泛型排序算法框架(選擇排序)
template<typename T>
void genericSelectionSort(T arr[], int n) {
    for (int i = 0; i < n-1; ++i) {
        int minIdx = i;
        for (int j = i+1; j < n; ++j)
            if (arr[j] < arr[minIdx]) minIdx = j;
        if (minIdx != i) mySwap(arr[i], arr[minIdx]);
    }
}

基于以上模板,可以輕松排序整數(shù)、浮點(diǎn)數(shù)、字符甚至自定義對(duì)象(需實(shí)現(xiàn)比較運(yùn)算符或提供仿函數(shù))。

Tips:仿函數(shù)(也稱(chēng)為函數(shù)對(duì)象)是C++中一個(gè)行為類(lèi)似函數(shù)對(duì)象。它通過(guò)重載函數(shù)調(diào)用運(yùn)算符operator()來(lái)實(shí)現(xiàn),使得對(duì)象可以像函數(shù)一樣被調(diào)用,這里就不多展開(kāi)了。

// 傳統(tǒng)比較函數(shù)
bool compareInts(int a, int b) {
    return a < b;  // 從小到大
}
// 仿函數(shù)比較器
class CompareInts {
public:
    bool operator()(int a, int b) const {
        return a < b;  // 從小到大
    }
};

?? 十一、模板的優(yōu)勢(shì)與運(yùn)行時(shí)開(kāi)銷(xiāo)

函數(shù)模板在編譯期進(jìn)行實(shí)例化,為每個(gè)類(lèi)型生成獨(dú)立函數(shù),因此沒(méi)有運(yùn)行時(shí)額外開(kāi)銷(xiāo),代碼效率和手寫(xiě)特定類(lèi)型函數(shù)相同。同時(shí)又能保證類(lèi)型安全,堪稱(chēng)C++泛型編程的基石。

?? 函數(shù)模板使得代碼極度精簡(jiǎn),同時(shí)又保留了極高的靈活性。在項(xiàng)目中,盡量將通用算法抽象為模板,減少重復(fù)勞動(dòng)。

?? 十二、思考與擴(kuò)展

1.如何修改選擇排序模板使其支持升序/降序通過(guò)函數(shù)參數(shù)控制?

可以從幾個(gè)層面來(lái)修改,使其功能更靈活:

方案一:增加布爾參數(shù) 這是最直接的修改。在函數(shù)參數(shù)列表中添加一個(gè)bool ascending(默認(rèn)為true表示升序),然后在內(nèi)部比較時(shí)根據(jù)這個(gè)參數(shù)決定是使用<(找最小值,用于升序)還是>(找最大值,用于降序)。

template<typename T>
void mySelectionSort_T(T arr[], int len, bool ascending = true) {
    for (int i = 0; i < len-1; i++) {
        int targetIdx = i; // 目標(biāo)位置應(yīng)存放的元素索引
        for (int j = i + 1; j < len; j++) {
            bool condition = ascending ? (arr[j] < arr[targetIdx]) : (arr[j] > arr[targetIdx]);
            if (condition) {
                targetIdx = j;
            }
        }
        if (targetIdx != i) {
            swap_T(arr[targetIdx], arr[i]);
        }
    }
}

使用示例: mySelectionSort_T(arr, len); // 默認(rèn)升序 mySelectionSort_T(arr, len, false); // 降序

方案二:增加枚舉參數(shù)(增強(qiáng)可讀性) 定義一個(gè)enum SortOrder { ASCENDING, DESCENDING };,用枚舉值代替布爾值,使調(diào)用意圖更明確。

mySelectionSort_T(arr, len, SortOrder::DESCENDING);

方案三:增加仿函數(shù)參數(shù)(最靈活、最符合C++標(biāo)準(zhǔn)庫(kù)風(fēng)格,推薦) 這是最強(qiáng)大和通用的方法。在模板中增加一個(gè)比較器參數(shù)Compare comp,默認(rèn)值可以設(shè)為std::less<T>()(表示升序)。在內(nèi)部比較時(shí),不再直接使用<>,而是調(diào)用比較器comp(arr[j], arr[targetIdx])。如果此調(diào)用返回true,則意味著arr[j]應(yīng)該排在arr[targetIdx]之前,因此更新targetIdx

template<typename T, typename Compare = std::less<T>>
void mySelectionSort_T(T arr[], int len, Compare comp = Compare()) {
    for (int i = 0; i < len-1; i++) {
        int targetIdx = i;
        for (int j = i + 1; j < len; j++) {
            if (comp(arr[j], arr[targetIdx])) { // 使用比較器
                targetIdx = j;
            }
        }
        if (targetIdx != i) {
            swap_T(arr[targetIdx], arr[i]);
        }
    }
}

使用示例

mySelectionSort_T(arr, len); // 默認(rèn)使用std::less,升序

mySelectionSort_T(arr, len, std::greater<int>()); // 使用std::greater,降序 

mySelectionSort_T(arr, len, [](int a, int b){ return a % 10 < b % 10; }); // 使用lambda表達(dá)式作為比較器自定義比較規(guī)則(按個(gè)位數(shù)升序排序)

mySelectionSort_T(arr, len, [](int a, int b){ return a % 10 > b % 10; }); // 使用lambda表達(dá)式作為比較器自定義比較規(guī)則(按個(gè)位數(shù)降序排序)

tips:無(wú)論是 std::less、std::greater還是自定義的函數(shù),在傳遞給像 std::sortmySelectionSort_T這樣的排序算法時(shí),都遵循同一個(gè)嚴(yán)格的語(yǔ)義約定

比較函數(shù) comp(a, b)返回 true唯一含義是:在算法所構(gòu)建的最終有序序列中,元素 a必須排在元素 b前面。

這個(gè)約定是排序算法能夠工作的基礎(chǔ)。算法并不關(guān)心 comp內(nèi)部是實(shí)現(xiàn)了“小于”還是“大于”,它只關(guān)心這個(gè)“前后順序”的規(guī)則。

比較器

比較操作

返回 true的含義

在排序中產(chǎn)生的效果

std::less<T>?

a < b

a(較小的) 應(yīng)排在 b(較大的) 前面?

升序? (小的在前,大的在后)

std::greater<T>?

a > b

a(較大的) 應(yīng)排在 b(較小的) 前面?

降序? (大的在前,小的在后)

簡(jiǎn)單記憶std::less-> “小者在前” -> 升序。std::greater-> “大者在前” -> 降序。

2.如果希望模板只接受具有operator<的類(lèi)型,應(yīng)該怎么做?

(C++20 Concept:template<typename T> requires std::totally_ordered<T>)

基于C++20的現(xiàn)代特性,我們可以這樣實(shí)現(xiàn):

std::totally_ordered<T> 是一個(gè)概念(Concept):它要求類(lèi)型T必須支持<<=>>=這四個(gè)完整的比較運(yùn)算符。如果只需要operator<,使用std::totally_ordered是足夠的,但可能約束“過(guò)強(qiáng)”。一個(gè)更精確的自定義概念可以是:

template<typename T>
concept HasLessThan = requires(const T& a, const T& b) {
    { a < b } -> std::convertible_to<bool>;
};
  • template<typename T>?:聲明一個(gè)模板參數(shù) T
  • concept HasLessThan =? :定義一個(gè)名為 HasLessThan的概念
  • requires(const T& a, const T& b)? :要求部分,定義需要滿(mǎn)足的條件
  • { a < b } -> std::convertible_to<bool>;? :具體的要求:a < b必須是一個(gè)有效的表達(dá)式,表達(dá)式的結(jié)果必須能轉(zhuǎn)換為 bool類(lèi)型

這個(gè)概念的語(yǔ)義是:類(lèi)型 T必須支持 <運(yùn)算符,并且這個(gè)運(yùn)算符的結(jié)果可以被用作布爾值。

概念(Concept)與模板函數(shù)的區(qū)別

特性

概念(Concept)

模板函數(shù)

本質(zhì)?

編譯時(shí)類(lèi)型約束,是謂詞(返回true/false)

編譯時(shí)代碼生成,是可執(zhí)行代碼?

作用?

檢查類(lèi)型是否滿(mǎn)足條件,不產(chǎn)生可執(zhí)行代碼

為特定類(lèi)型生成函數(shù)實(shí)例,產(chǎn)生可執(zhí)行代碼

返回值?

編譯時(shí)布爾值(類(lèi)型是否滿(mǎn)足條件)

運(yùn)行時(shí)返回值(函數(shù)執(zhí)行結(jié)果)

然后,在函數(shù)模板聲明中使用它進(jìn)行約束:

template <HasLessThan T> // 或者 template <std::totally_ordered T>
void mySelectionSort_T(T arr[], int len) {
    // ... 函數(shù)體內(nèi)部可以安全地使用 arr[j] < arr[targetIdx] ...
}

這樣,當(dāng)用戶(hù)嘗試用不支持operator<的類(lèi)型實(shí)例化此模板時(shí),編譯器會(huì)在調(diào)用處給出清晰易懂的錯(cuò)誤信息,而不是在函數(shù)體內(nèi)部遇到<符號(hào)時(shí)才報(bào)出令人困惑的錯(cuò)誤。這是C++20引入Concepts的主要優(yōu)勢(shì)之一:將接口約束檢查提前,并改善錯(cuò)誤信息。

3.嘗試實(shí)現(xiàn)一個(gè)模板函數(shù)findMax,返回?cái)?shù)組中最大元素的索引。

這個(gè)函數(shù)可以看作是mySelectionSort_T函數(shù)中“尋找最大值”步驟的獨(dú)立版本,實(shí)現(xiàn)起來(lái)非常直觀:

/// 查找數(shù)組中最大元素的索引
/// @param arr 數(shù)組指針
/// @param len 數(shù)組長(zhǎng)度
/// @return 最大元素的索引,如果數(shù)組為空(len<=0)則返回-1
template<typename T>
int findMax(T arr[], int len) {
    if (len <= 0) return -1; // 處理邊界情況

    int maxIdx = 0; // 假設(shè)第一個(gè)元素最大
    for (int i = 1; i < len; ++i) {
        if (arr[maxIdx] < arr[i]) { // 如果發(fā)現(xiàn)更大的元素
            maxIdx = i; // 更新最大元素索引
        }
    }
    return maxIdx;
}

增強(qiáng)版本:可以像排序函數(shù)一樣,為其增加一個(gè)比較器參數(shù),使其更加通用,可以用于尋找“在某種比較規(guī)則下的極值”。

template<typename T, typename Compare = std::less<T>>
int findMax(T arr[], int len, Compare comp = Compare()) {
    if (len <= 0) return -1;
    int extremeIdx = 0;
    for (int i = 1; i < len; ++i) {
        if (comp(arr[extremeIdx], arr[i])) { // 如果當(dāng)前“極值”排在 arr[i] 之前
            extremeIdx = i; // 更新為新的“極值”
        }
    }
    return extremeIdx;
}
// 使用:findMax(arr, len) 找最大, findMax(arr, len, std::greater<int>()) 找最小。

4.模板特化和函數(shù)重載有何區(qū)別?何時(shí)優(yōu)先使用特化?

這是一個(gè)核心概念,代碼已經(jīng)同時(shí)包含了兩種用法(printArr的特化和重載),正好可以用來(lái)對(duì)比。

核心區(qū)別

特性模板特化函數(shù)重載
定義一個(gè)已有的主模板特定類(lèi)型參數(shù)提供一個(gè)完全特殊的實(shí)現(xiàn)。定義多個(gè)同名但參數(shù)列表不同獨(dú)立函數(shù)。
關(guān)系是主模板的“特例”,語(yǔ)法上緊密綁定(需要template<>)。函數(shù)之間是平等的“重載”關(guān)系,彼此獨(dú)立。
匹配時(shí)機(jī)模板參數(shù)推導(dǎo)/確定之后發(fā)生。編譯器先決定調(diào)用哪個(gè)模板,再檢查是否有特化。重載決議階段發(fā)生。編譯器在所有候選函數(shù)(包括普通函數(shù)和可能推導(dǎo)出的模板)中選擇最匹配的一個(gè)。
靈活性主要是為特定類(lèi)型定制行為,通常保持相同的函數(shù)簽名和語(yǔ)義。可以改變參數(shù)數(shù)量、類(lèi)型,甚至語(yǔ)義(但通常不推薦改變語(yǔ)義)。

代碼中的例子

  • template<> void printArr<int>(int arr[], int len) 是模板特化。它是對(duì)主模板template<typename T> void printArr(T arr[], int len)T=int時(shí)的特殊實(shí)現(xiàn)。
  • void printArr(int arr[], int len) 是一個(gè)普通函數(shù)重載。它是一個(gè)獨(dú)立的函數(shù),與模板無(wú)關(guān)。當(dāng)調(diào)用printArr(int_arr, num1)時(shí),根據(jù)您提到的優(yōu)先級(jí)規(guī)則(完全匹配的普通函數(shù) > 模板),它會(huì)優(yōu)先于模板被調(diào)用。

何時(shí)優(yōu)先使用特化?

  1. 定制類(lèi)模板行為時(shí):這是最常見(jiàn)場(chǎng)景。函數(shù)重載不能用于類(lèi),只能對(duì)類(lèi)模板進(jìn)行(全/偏)特化。例如,為std::vector<bool>做特化優(yōu)化。
  2. 希望行為變化對(duì)用戶(hù)“透明”時(shí):當(dāng)你想為某種類(lèi)型提供特殊實(shí)現(xiàn),但希望函數(shù)簽名、名稱(chēng)和核心語(yǔ)義保持不變,對(duì)所有調(diào)用者來(lái)說(shuō),調(diào)用方式完全一樣,只是內(nèi)部效率或細(xì)節(jié)不同。例如,為std::swap特化自定義類(lèi),使其交換更高效。
  3. 針對(duì)一類(lèi)類(lèi)型(通過(guò)偏特化,僅限類(lèi)模板):類(lèi)模板支持偏特化(如template<typename T> class Widget<T*>),可以為某一類(lèi)類(lèi)型(如所有指針)提供特殊實(shí)現(xiàn),這是函數(shù)重載無(wú)法直接做到的。

總結(jié)建議: 代碼注釋中“提供了函數(shù)模板,最好就不要提供普通函數(shù),否則容易出現(xiàn)二義性”的觀點(diǎn)非常重要。在函數(shù)層面,一個(gè)更通用的建議是:如果主模板的邏輯對(duì)大多數(shù)類(lèi)型都有效,僅為少數(shù)類(lèi)型需要特殊處理,且處理邏輯是“優(yōu)化”或“微小調(diào)整”,考慮使用特化。如果針對(duì)不同類(lèi)型需要的是邏輯完全不同、甚至簽名都不同的操作,那么應(yīng)該使用函數(shù)重載。 在實(shí)際中,對(duì)于函數(shù)模板,使用“帶約束的模板”或“標(biāo)簽分發(fā)”等技巧有時(shí)比特化更清晰。C++20的Concepts進(jìn)一步增強(qiáng)了這種能力。

?? 附1:示例來(lái)自如下實(shí)際可運(yùn)行的 .cpp 文件

#include <iostream>
/// <summary>
/// 表示平面坐標(biāo)系中的點(diǎn)的類(lèi)
/// </summary>
class MyPoint {
    double x;
    double y;
public:
    MyPoint(double x, double y) { this->x = x; this->y = y; }
    double getX()const { return x; }
    double getY()const { return y; }
    void show()const { std::cout << '(' << x << ',' << y << ')'; }
};
/// <summary>
/// 打印數(shù)組函數(shù)(模板函數(shù))
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="arr"></param>
/// <param name="len"></param>
template<typename T>
void printArr(T arr[], int len) {
    for (int i = 0; i < len; i++) {
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;
}
/// <summary>
/// 打印整型數(shù)組函數(shù)(模板特化)
/// </summary>
/// <param name="arr"></param>
/// <param name="len"></param>
template<>
void printArr<int>(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        std::cout << arr[i] << " / ";
    }
    std::cout << std::endl;
}
/// <summary>
/// 打印坐標(biāo)點(diǎn)數(shù)組函數(shù)(模板特化)
/// 模板的通用性并不是萬(wàn)能的,比如兩個(gè)數(shù)組直接賦值或者自定義數(shù)據(jù)類(lèi)型操作,所以需要特化
/// 有參數(shù)可以自動(dòng)推導(dǎo)為MyPoint類(lèi)型,調(diào)用這個(gè)特化版本來(lái)打印坐標(biāo)點(diǎn)數(shù)組
/// 也可以顯式指定模板參數(shù)為MyPoint,void printArr<MyPoint>(MyPoint arr[], int len)
/// </summary>
/// <param name="arr"></param>
/// <param name="len"></param>
template<>
void printArr(MyPoint arr[], int len) {
    for (int i = 0; i < len; i++) {
        std::cout << "(" << arr[i].getX() << "," << arr[i].getY() << ")";
    }
    std::cout << std::endl;
}
/// <summary>
/// 打印數(shù)組函數(shù),連接符自定義(模板函數(shù))
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="arr"></param>
/// <param name="len"></param>
/// <param name="hyphen"></param>
template<typename T>
void printArr(T arr[], int len, char hyphen) {
    for (int i = 0; i < len; i++) {
        std::cout << arr[i] << hyphen;
    }
    std::cout << std::endl;
}
/// <summary>
/// 打印數(shù)組函數(shù),連接符固定為逗號(hào)(普通函數(shù))
/// 如果函數(shù)模板和普通函數(shù)都可以實(shí)現(xiàn),優(yōu)先調(diào)用普通函數(shù)
/// 但是提供了函數(shù)模板,最好就不要提供普通函數(shù),否則容易出現(xiàn)二義性
/// </summary>
/// <param name="arr"></param>
/// <param name="len"></param>
void printArr(int arr[], int len) {
    for (int i = 0; i < len; i++) {
        std::cout << arr[i] << ",";
    }
    std::cout << std::endl;
}
/// <summary>
/// 交換整型函數(shù)
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
void swapInt(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}
/// <summary>
/// 整型加函數(shù)
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
int addInt(int a, int b) {
	return a + b;
}
/// <summary>
/// 交換浮點(diǎn)型函數(shù)
/// </summary>
/// <param name="a"></param>
/// <param name="b"></param>
void swapDouble(double& a, double& b) {
    double temp = a;
    a = b;
    b = temp;
}
/// <summary>
/// 交換函數(shù)(模板函數(shù))
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="a"></param>
/// <param name="b"></param>
template<typename T>
void swap_T(T& a, T& b) {
    T temp = a;
    a = b;
    b = temp;
}
/// <summary>
/// 加函數(shù)(模板函數(shù))
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="a"></param>
/// <param name="b"></param>
/// <returns></returns>
template<class T>
T add_T(T a, T b)
{
    return a + b;
}
/// <summary>
/// 無(wú)參數(shù)模板函數(shù)測(cè)試
/// </summary>
/// <typeparam name="T"></typeparam>
template<typename T>
void test_T() {
	//初始化T類(lèi)型的變量,使用{}值初始化
    //1. 安全性第一:防止未初始化變量
    //2. 防止窄化轉(zhuǎn)換(編譯時(shí)錯(cuò)誤)
    //3. 統(tǒng)一性:一套語(yǔ)法適用于所有類(lèi)型
    //4. 區(qū)分函數(shù)聲明和對(duì)象構(gòu)造
    //5. 清晰的語(yǔ)義表達(dá)
    //6. 模板的通用性:適用更多類(lèi)型(例如自定義類(lèi)、指針、僅有 explicit構(gòu)造函數(shù)的類(lèi)型等)
    T temp{};
    std::cout << "測(cè)試:" << temp << std::endl;
    T temp1{0};
    std::cout << "測(cè)試:" << temp << std::endl;
    //T temp2{0.5};//從“double”轉(zhuǎn)換到“int”需要收縮轉(zhuǎn)換
	//T的類(lèi)型由顯示指定,而不是自動(dòng)類(lèi)型推導(dǎo),所以T的類(lèi)型不是浮點(diǎn)型,而是int類(lèi)型
    temp = 1.5;
    std::cout << "測(cè)試:" << temp << std::endl;
}
/// <summary>
/// 測(cè)試函數(shù)
/// </summary>
void test1() {
    std::cout << "==============================================================" << std::endl;
    std::cout << "函數(shù)模板封裝交換函數(shù)測(cè)試:" << std::endl;
    int a = 10;
    int b = 20;
    char c = 'c';//'c' 對(duì)應(yīng) ASCII碼 99
    double d = 0.5;
	std::cout << "交換前:a = " << a << ", b = " << b << ", c = " << c << ", d = " << d << std::endl;
    std::cout << "=== 普通函數(shù) ===" << std::endl;
	//普通函數(shù)交換
    swapInt(a, b);
	std::cout << "交換后:a = " << a << ", b = " << b << std::endl;
	//隱式轉(zhuǎn)換允許不同類(lèi)型的變量交換
    //swapInt(a, c);// 引用傳遞 - 不允許隱式類(lèi)型轉(zhuǎn)換,引用應(yīng)該對(duì)原變量的直接操作,而轉(zhuǎn)換會(huì)產(chǎn)生臨時(shí)變量
    //但是const引用允許隱式轉(zhuǎn)換,const引用是只讀的,綁定到臨時(shí)變量是安全的,因?yàn)椴粫?huì)修改它。
    std::cout  << a << "+ " <<c << "= " << addInt(a, c) << std::endl;// 值傳遞 - 允許隱式類(lèi)型轉(zhuǎn)換
    std::cout << "=== 有參數(shù)版本(可以自動(dòng)推導(dǎo))===" << std::endl;
	//模板函數(shù)交換
    swap_T(a, b);
	std::cout << "交換后:a = " << a << ", b = " << b << std::endl;
	//顯示指定類(lèi)型
    swap_T<int>(a, b);
	std::cout << "交換后:a = " << a << ", b = " << b << std::endl;
    //swap_T<int>(a, d);//引用傳遞同樣不允許隱式類(lèi)型轉(zhuǎn)換
    //隱式轉(zhuǎn)換
    std::cout << "相加:a = " << a << ", d = " << d << std::endl;
    std::cout << a << "+ " << d << "= " << add_T<int>(a, d) << std::endl;//顯式指定,可以隱式轉(zhuǎn)換
    //std::cout << a << "+ " << d << "= " << add_T(a, d) << std::endl;//自動(dòng)推導(dǎo),不可以隱式轉(zhuǎn)換
	//自動(dòng)類(lèi)型推導(dǎo)不允許不同類(lèi)型的變量
    // 推導(dǎo)過(guò)程:
    //1. 看第一個(gè)參數(shù)a → T必須與a類(lèi)型匹配 → T = int
    //2. 看第二個(gè)參數(shù)d → T必須與d類(lèi)型匹配 → T = double
    //3. 矛盾!T不能既是int又是double
    //4. 推導(dǎo)失敗,編譯錯(cuò)誤
    //swap_T(a, d);
    std::cout << "=== 無(wú)參數(shù)版本(必須顯式指定)===" << std::endl;
	//必須顯式指定類(lèi)型
    //test_T();
    test_T<int>();
    std::cout << "==============================================================" << std::endl;
}
/// <summary>
/// 選擇排序函數(shù)(模板函數(shù))
/// 不穩(wěn)定排序,可能改變相同元素相對(duì)順序,因?yàn)闀?huì)遠(yuǎn)距離交換
/// 時(shí)間復(fù)雜度:始終O(n2)
/// 空間復(fù)雜度:O(1)原地排序
/// 比較次數(shù)?:固定:n(n - 1) / 2
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="arr"></param>
/// <param name="len"></param>
template<typename T>
void mySelectionSort_T(T arr[],int len) {
    // 外層循環(huán):控制放置位置
    for (int i = 0; i < len-1; i++) {
        int max = i;
        // 內(nèi)層循環(huán):從前往后在未排序部分尋找最大值
        for (int j = i + 1; j < len; j++) {
            if (arr[max] < arr[j]) {
                max = j;
            }
        }
        std::cout << "i = " << i << ", max = " << max << std::endl;
        // 內(nèi)層循環(huán)結(jié)束后,交換一次,每次都挑出最大值
        if (max != i) {
            swap_T(arr[max], arr[i]);
            std::cout << "交換:" << std::endl;
            printArr(arr, len);
        }
    }
}
/// <summary>
/// 冒泡排序函數(shù)(模板函數(shù))
/// 穩(wěn)定排序,不改變相同元素相對(duì)順序,因?yàn)橹粫?huì)相鄰交換
/// 時(shí)間復(fù)雜度:平均O(n2),最優(yōu)O(n)
/// 空間復(fù)雜度:O(1)原地排序
/// 比較次數(shù)?:平均n(n-1)/2,最優(yōu)n-1
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="arr"></param>
/// <param name="len"></param>
template<typename T>
void myBubbleSort_T(T arr[], int len) {
    // 外層循環(huán):控制冒泡輪數(shù)
    for (int i = 0; i < len-1; i++) {
        bool swapped = false;//退出邏輯優(yōu)化
        // 內(nèi)層循環(huán):從后往前每輪減少比較范圍執(zhí)行相鄰比較
        for (int j = 0; j < len-1-i; j++) {
            //每輪內(nèi)循環(huán):讓一個(gè)小氣泡上浮到水面(從大到小排序)
            std::cout << "i = " << i << ", j = " << j << std::endl;
            if (arr[j] < arr[j+1]) {
                //立即交換
                swap_T(arr[j], arr[j+1]);
                swapped = true;
                std::cout << "交換:" << std::endl;
                printArr(arr, len);
            }
        }
        if (!swapped) {
            break;//如果沒(méi)有交換,說(shuō)明已經(jīng)有序,提前退出
            std::cout << "已經(jīng)有序,提前退出" << std::endl;
        }
    }
}
/// <summary>
/// 測(cè)試函數(shù)
/// </summary>
void test2() {
    std::cout << "==============================================================" << std::endl;
    std::cout << "函數(shù)模板封裝選擇排序函數(shù),char數(shù)組測(cè)試:"<< std::endl;
	char char_arr[] = "hello world";//雙引號(hào)括起的字符串會(huì)自動(dòng)在末尾添加空字符'\0',所以長(zhǎng)度12
    int num = sizeof(char_arr) / sizeof(char);
    std::cout << "輸入數(shù)組元素?cái)?shù)量:" << num << std::endl;
    //函數(shù)模板重載測(cè)試
    printArr(char_arr, num);
    printArr(char_arr, num,'-');
    mySelectionSort_T(char_arr, num);
    printArr(char_arr, num);
    std::cout << "==============================================================" << std::endl;
}
/// <summary>
/// 測(cè)試函數(shù)
/// </summary>
void test3() {
    std::cout << "==============================================================" << std::endl;
    std::cout << "函數(shù)模板封裝選擇排序函數(shù),int數(shù)組測(cè)試:" << std::endl;
    //int int_arr[] = {7,5,3,1,5,9,4,5,6,8,5,2 };
    int int_arr[] = { 1,2,3,4 };
    int num = sizeof(int_arr) / sizeof(int);
    std::cout << "輸入數(shù)組元素?cái)?shù)量:" << num << std::endl;
    printArr<>(int_arr, num);//空模板參數(shù)列表來(lái)強(qiáng)制調(diào)用函數(shù)模板
    mySelectionSort_T(int_arr, num);
    printArr(int_arr, num);
    std::cout << "==============================================================" << std::endl;
}
/// <summary>
/// 測(cè)試函數(shù)
/// </summary>
void test4() {
    std::cout << "==============================================================" << std::endl;
    std::cout << "函數(shù)模板封裝冒泡排序函數(shù),int數(shù)組測(cè)試:" << std::endl;
    //int int_arr[] = {7,5,3,1,5,9,4,5,6,8,5,2 };
    int int_arr[] = { 1,2,3,4 };
    int num = sizeof(int_arr) / sizeof(int);
    std::cout << "輸入數(shù)組元素?cái)?shù)量:" << num << std::endl;
    printArr(int_arr, num);
    myBubbleSort_T(int_arr, num);
    printArr(int_arr, num);
    std::cout << "==============================================================" << std::endl;
}
/// <summary>
/// 測(cè)試函數(shù)
/// 函數(shù)調(diào)用優(yōu)先級(jí):
/// 1.完全匹配的普通函數(shù)(最高優(yōu)先級(jí))
/// 2.模板特化
/// 3.函數(shù)模板
/// 4.隱式轉(zhuǎn)換的普通函數(shù)(最低優(yōu)先級(jí))
/// </summary>
void test5() {
    std::cout << "==============================================================" << std::endl;
    std::cout << "函數(shù)調(diào)用優(yōu)先級(jí)測(cè)試:" << std::endl;
    int int_arr[] = { 1,2,3,4 };
    int num1 = sizeof(int_arr) / sizeof(int);
    std::cout << "輸入數(shù)組元素?cái)?shù)量:" << num1 << std::endl;
    printArr(int_arr, num1);
    printArr<>(int_arr, num1);//空模板參數(shù)列表來(lái)強(qiáng)制調(diào)用函數(shù)模板
	MyPoint point_arr[] = { MyPoint(1,2), MyPoint(3,4), MyPoint(5,6) };
    int num2 = sizeof(point_arr) / sizeof(MyPoint);
    std::cout << "輸入數(shù)組元素?cái)?shù)量:" << num2 << std::endl;
    printArr(point_arr, num2);
    std::cout << "==============================================================" << std::endl;
}
int main()
{
	//調(diào)用測(cè)試函數(shù)
    test1();
    test2();
    test3();
    test4();
    test5();
	//等待用戶(hù)輸入
    system("pause");
	//返回0表示程序正常結(jié)束
    return 0;
}

?? 附2:程序運(yùn)行輸出

==============================================================
函數(shù)模板封裝交換函數(shù)測(cè)試:
交換前:a = 10, b = 20, c = c, d = 0.5
=== 普通函數(shù) ===
交換后:a = 20, b = 10
20+ c= 119
=== 有參數(shù)版本(可以自動(dòng)推導(dǎo))===
交換后:a = 10, b = 20
交換后:a = 20, b = 10
相加:a = 20, d = 0.5
20+ 0.5= 20
=== 無(wú)參數(shù)版本(必須顯式指定)===
測(cè)試:0
測(cè)試:0
測(cè)試:1
==============================================================
==============================================================
函數(shù)模板封裝選擇排序函數(shù),char數(shù)組測(cè)試:
輸入數(shù)組元素?cái)?shù)量:12
h e l l o   w o r l d
h-e-l-l-o- -w-o-r-l-d--
i = 0, max = 6
交換:
w e l l o   h o r l d
i = 1, max = 8
交換:
w r l l o   h o e l d
i = 2, max = 4
交換:
w r o l l   h o e l d
i = 3, max = 7
交換:
w r o o l   h l e l d
i = 4, max = 4
i = 5, max = 7
交換:
w r o o l l h   e l d
i = 6, max = 9
交換:
w r o o l l l   e h d
i = 7, max = 9
交換:
w r o o l l l h e   d
i = 8, max = 8
i = 9, max = 10
交換:
w r o o l l l h e d
i = 10, max = 10
w r o o l l l h e d
==============================================================
==============================================================
函數(shù)模板封裝選擇排序函數(shù),int數(shù)組測(cè)試:
輸入數(shù)組元素?cái)?shù)量:4
1 / 2 / 3 / 4 /
i = 0, max = 3
交換:
4,2,3,1,
i = 1, max = 2
交換:
4,3,2,1,
i = 2, max = 2
4,3,2,1,
==============================================================
==============================================================
函數(shù)模板封裝冒泡排序函數(shù),int數(shù)組測(cè)試:
輸入數(shù)組元素?cái)?shù)量:4
1,2,3,4,
i = 0, j = 0
交換:
2,1,3,4,
i = 0, j = 1
交換:
2,3,1,4,
i = 0, j = 2
交換:
2,3,4,1,
i = 1, j = 0
交換:
3,2,4,1,
i = 1, j = 1
交換:
3,4,2,1,
i = 2, j = 0
交換:
4,3,2,1,
4,3,2,1,
==============================================================
==============================================================
函數(shù)調(diào)用優(yōu)先級(jí)測(cè)試:
輸入數(shù)組元素?cái)?shù)量:4
1,2,3,4,
1 / 2 / 3 / 4 /
輸入數(shù)組元素?cái)?shù)量:3
(1,2)(3,4)(5,6)
==============================================================

到此這篇關(guān)于C++ 函數(shù)模板完全指南(最新整理)的文章就介紹到這了,更多相關(guān)C++ 函數(shù)模板內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C語(yǔ)言文件操作中 fgets與fputs 函數(shù)詳解

    C語(yǔ)言文件操作中 fgets與fputs 函數(shù)詳解

    這篇文章主要介紹了C語(yǔ)言文件操作中 fgets與fputs 函數(shù)詳解的相關(guān)資料,需要的朋友可以參考下
    2017-06-06
  • C++11 中的std::function和std::bind詳解

    C++11 中的std::function和std::bind詳解

    這篇文章主要介紹了C++ 11 std::function和std::bind,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-10-10
  • QT實(shí)現(xiàn)簡(jiǎn)單音樂(lè)播放器

    QT實(shí)現(xiàn)簡(jiǎn)單音樂(lè)播放器

    這篇文章主要為大家詳細(xì)介紹了QT實(shí)現(xiàn)簡(jiǎn)單的音樂(lè)播放器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • c++string字符串的比較是否相等問(wèn)題

    c++string字符串的比較是否相等問(wèn)題

    這篇文章主要介紹了c++string字符串的比較是否相等問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • C++ 繼承詳解及實(shí)例代碼

    C++ 繼承詳解及實(shí)例代碼

    這篇文章主要介紹了C++ 繼承詳解,這里整理了詳細(xì)的資料及實(shí)例代碼,有需要的小伙伴可以參考下
    2016-09-09
  • C++空類(lèi)默認(rèn)函數(shù)詳細(xì)解析

    C++空類(lèi)默認(rèn)函數(shù)詳細(xì)解析

    如果你只是聲明一個(gè)空類(lèi),不做任何事情的話,編譯器會(huì)自動(dòng)為你生成一個(gè)默認(rèn)構(gòu)造函數(shù)、一個(gè)拷貝默認(rèn)構(gòu)造函數(shù)、一個(gè)默認(rèn)拷貝賦值操作符和一個(gè)默認(rèn)析構(gòu)函數(shù)
    2013-10-10
  • C語(yǔ)言實(shí)現(xiàn)掃雷游戲詳細(xì)代碼

    C語(yǔ)言實(shí)現(xiàn)掃雷游戲詳細(xì)代碼

    這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)掃雷游戲的具體步驟和詳細(xì)代碼,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-11-11
  • C語(yǔ)言動(dòng)態(tài)內(nèi)存分配的詳解

    C語(yǔ)言動(dòng)態(tài)內(nèi)存分配的詳解

    這篇文章主要介紹了C語(yǔ)言動(dòng)態(tài)內(nèi)存分配的詳解的相關(guān)資料,這里提供了實(shí)現(xiàn)方法整理和出現(xiàn)錯(cuò)誤的解決辦法,需要的朋友可以參考下
    2017-07-07
  • C語(yǔ)言實(shí)現(xiàn) 數(shù)據(jù)類(lèi)型占多少字節(jié)指針占多少字節(jié)

    C語(yǔ)言實(shí)現(xiàn) 數(shù)據(jù)類(lèi)型占多少字節(jié)指針占多少字節(jié)

    這篇文章主要介紹了 C語(yǔ)言 數(shù)據(jù)類(lèi)型占多少字節(jié)指針占多少字節(jié)的實(shí)例代碼,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • C/C++中輸入多組數(shù)據(jù)的方法

    C/C++中輸入多組數(shù)據(jù)的方法

    這篇文章主要為大家詳細(xì)介紹了C/C++中輸入多組數(shù)據(jù)的三種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-08-08

最新評(píng)論

通化市| 南皮县| 滨州市| 祁连县| 东安县| 安西县| 泽库县| 禹城市| 凤阳县| 延吉市| 都匀市| 调兵山市| 自贡市| 西乌珠穆沁旗| 蚌埠市| 黔东| 密山市| 英山县| 兴业县| 延安市| 海盐县| 呼和浩特市| 集贤县| 防城港市| 遂溪县| 菏泽市| 牟定县| 麻江县| 佛山市| 九江县| 托里县| 曲周县| 南宁市| 昌吉市| 分宜县| 河源市| 蚌埠市| 沅江市| 珲春市| 友谊县| 金山区|