成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

告別懵圈!一文徹底搞懂 C++ 模板的類型與非類型參數(shù) (附源碼解析)

開(kāi)發(fā)
模板是 C++ 強(qiáng)大泛型編程能力的基石,理解了它的參數(shù),就基本掌握了開(kāi)啟這扇大門(mén)的鑰匙。

很多人 一聽(tīng)到"模板"就覺(jué)得頭大,覺(jué)得那是 C++ 黑魔法,只有編譯器開(kāi)發(fā)者才能玩得轉(zhuǎn)。其實(shí),模板是 C++ 強(qiáng)大泛型編程能力的基石,理解了它的參數(shù),就基本掌握了開(kāi)啟這扇大門(mén)的鑰匙。

一、 為啥要有模板參數(shù)

想象一下,你要寫(xiě)一個(gè)函數(shù),計(jì)算兩個(gè)整數(shù)的和。很簡(jiǎn)單:

int add(int a, int b) 
{
    return a + b;
}

然后,產(chǎn)品經(jīng)理跑過(guò)來(lái)說(shuō):"我們還需要計(jì)算兩個(gè) double 的和!" 于是你復(fù)制粘貼,改類型:

double add(double a, double b) 
{
return a + b;
}

接著,他又來(lái)了:"float 也要!"、"long long 也不能少!"… 你是不是想打人?代碼重復(fù),維護(hù)困難,簡(jiǎn)直是噩夢(mèng)。

這時(shí)候,C++ 模板閃亮登場(chǎng)!它就像一個(gè)"代碼生成器"的藍(lán)圖。你告訴它:"嘿,我要一個(gè) add 函數(shù),它能處理某種類型的數(shù)據(jù),具體是啥類型,等我用的時(shí)候再告訴你!"

template <typename T> // T 就是一個(gè)“類型參數(shù)”
T add(T a, T b) 
{
    return a + b;
}

看到 <typename T> 了嗎?

這里的 T 就是我們今天的主角之一:類型參數(shù)。它像一個(gè)占位符,代表"任何一種類型"。當(dāng)你調(diào)用 add<int>(3, 5) 時(shí),編譯器心領(lǐng)神會(huì):"哦,用戶指定 T 是 int",然后咔咔咔在背后幫你生成了 int add(int, int) 的版本。你調(diào)用 add<double>(3.14, 2.71),它又生成 double add(double, double) 的版本。一份代碼,N 種用途,爽不爽?

這就是模板參數(shù)存在的意義:讓代碼更通用、更靈活,減少重復(fù),提高復(fù)用性,并且這一切通常在編譯時(shí)完成,不損失運(yùn)行時(shí)性能。

初學(xué)者可能想問(wèn):這里的 T 是固定的嗎?必須是 T 嗎?

T 并不是一個(gè)固定的名稱,而是一個(gè)占位符類型名,由開(kāi)發(fā)者自行定義。可以用任何合法的標(biāo)識(shí)符(如 U, Type, MyType 等)替換 T

例如:

template <typename MyType>
void print(MyType value) {
    // MyType 是自定義的類型占位符
}

T 是約定俗成的默認(rèn)名稱(類似循環(huán)中的 i),但并非強(qiáng)制。

在復(fù)雜場(chǎng)景中,我更喜歡使用具有描述性的名稱(如 KeyType, ValueType)。

基礎(chǔ)回顧完畢,現(xiàn)在咱們正式深入了解這兩類參數(shù)。

二、 類型參數(shù)

類型參數(shù),顧名思義,就是用來(lái)指定一個(gè)類型的參數(shù)。它是模板中最常見(jiàn)、最基礎(chǔ)的參數(shù)。

聲明方式:通常使用 typename 或 class 關(guān)鍵字來(lái)聲明。這兩個(gè)關(guān)鍵字在這里是完全等價(jià)的,看個(gè)人或團(tuán)隊(duì)喜好。

template <typename T> 
class MyContainer { /* ... */ };

template <class U> 
void process(U data) { /* ... */ };

template <typename Key, class Value> 
class MyMap { /* ... */ }; // 可以有多個(gè)

作用:它允許你在定義模板時(shí),將具體的類型"延后"決定。你可以用這個(gè)參數(shù)來(lái)定義成員變量的類型、函數(shù)參數(shù)的類型、返回值的類型等等。

1. 實(shí)戰(zhàn)演練:扒一扒 std::vector 的源碼(概念層面)

一起來(lái)看下 C++ 標(biāo)準(zhǔn)庫(kù)里的老大哥 std::vector。它的基本形態(tài)(簡(jiǎn)化版)大概是這樣的:

// (概念性簡(jiǎn)化,非完整源碼)
namespace std {
    template <typename T, typename Allocator = std::allocator<T>> // 看這里!T 和 Allocator 都是類型參數(shù)
    class vector {
    public:
        using value_type = T; // 用 T 定義內(nèi)部類型別名
        using allocator_type = Allocator;
        using pointer = typename std::allocator_traits<Allocator>::pointer; // T 通過(guò) Allocator 影響指針類型
        using reference = value_type&; // T 定義引用類型
        // ... 構(gòu)造函數(shù)、析構(gòu)函數(shù)等 ...
        void push_back(const T& value); // 函數(shù)參數(shù)類型是 T
        void push_back(T&& value);      // 重載版本,參數(shù)類型也是 T
        reference operator[](size_t n); // 返回值類型是 T 的引用
        const_reference operator[](size_t n) const; // const 版本
        // ... 其他成員函數(shù) ...
    private:
        Allocator alloc; // 成員變量,類型是 Allocator
        pointer data_start; // 指針,其指向的類型最終由 T 和 Allocator 決定
        pointer data_end;
        pointer storage_end;
        // ... 內(nèi)部輔助函數(shù),會(huì)大量使用 T 和 Allocator ...
        void reallocate(); // 內(nèi)部實(shí)現(xiàn)會(huì)用到 allocator 分配 T 類型的內(nèi)存
    };
}

(1) typename T: 

這是最核心的類型參數(shù)。它決定了 vector 容器里存儲(chǔ)的元素是什么類型。你想存 int?那就 std::vector<int>,此時(shí)模板內(nèi)所有的 T 都被替換成 int。你想存 std::string?那就 std::vector<std::string>,T 就變成了 std::string。甚至可以存自定義的類 MyClass,寫(xiě)成 std::vector<MyClass>。T 的靈活性讓 vector 成為了一個(gè)“萬(wàn)能容器”。

(2) typename Allocator = std::allocator<T>: 

這是第二個(gè)類型參數(shù),代表內(nèi)存分配器的類型。它稍微高級(jí)一點(diǎn),還帶了個(gè)默認(rèn)值 std::allocator<T>。這意味著如果你不指定第二個(gè)參數(shù)(像我們平時(shí)那樣 std::vector<int>),編譯器就默認(rèn)使用標(biāo)準(zhǔn)的內(nèi)存分配器。但如果你有特殊需求,比如想用自定義的內(nèi)存池,你可以提供自己的分配器類型:std::vector<int, MyCoolAllocator<int>>。注意,這個(gè) Allocator 類型本身也經(jīng)常是模板,并且它的行為通常也依賴于 T(比如 std::allocator<T> 需要知道要分配多大的內(nèi)存,這取決于 T 的大小)。

小結(jié)類型參數(shù):

  • 它是模板的“靈魂”,決定了模板實(shí)例化的“材質(zhì)”或“內(nèi)容類型”。
  • 使用 typename 或 class 聲明。
  • 極大地提高了代碼的泛用性。
  • 幾乎所有泛型容器、算法的核心都依賴于類型參數(shù)。

三、 非類型參數(shù)

再看看非類型參數(shù),也叫值參數(shù)(Value Parameters)。

聲明方式:直接聲明一個(gè)帶有具體類型的變量名。這個(gè)類型必須是編譯時(shí)常量能確定的類型。

常見(jiàn)的允許類型包括:

  • 整型或枚舉類型 (int, unsigned int, char, bool, enum 等)
  • 指針類型 (指向?qū)ο蠡蚝瘮?shù)的指針,包括成員指針)
  • 左值引用類型 (指向?qū)ο蠡蚝瘮?shù)的引用)
  • std::nullptr_t (C++11 起)
  • auto (C++17 起,編譯器自動(dòng)推導(dǎo)類型,但必須是上述允許的類型之一)
  • C++20 起,還允許特定的類類型(字面值常量類,Literal Class Types)和浮點(diǎn)數(shù)(需要編譯器支持和特定選項(xiàng)),這個(gè)感興趣的另外去學(xué)習(xí)。

作用:它允許你在模板實(shí)例化時(shí),傳遞一個(gè)編譯時(shí)常量值。這個(gè)值會(huì)成為模板定義內(nèi)部的一個(gè)常量,可以用來(lái)決定數(shù)組大小、循環(huán)次數(shù)、作為 switch case 的標(biāo)簽、或者用于某些需要編譯時(shí)常量的計(jì)算中。

實(shí)戰(zhàn)演練 1:穩(wěn)如磐石的 std::array

std::vector 的大小是運(yùn)行時(shí)動(dòng)態(tài)變化的,而 C++11 引入的 std::array 則代表了固定大小的數(shù)組。它是如何做到固定大小的呢?答案就在非類型參數(shù)!

// (概念性簡(jiǎn)化)
namespace std {
    template <typename T, std::size_t N> // T 是類型參數(shù),N 是非類型參數(shù)!
    struct array {
        // 使用 T
        using value_type = T;
        // ... 其他類型別名 ...
        // 關(guān)鍵:內(nèi)部存儲(chǔ),大小由 N 決定!
        T _elements[N]; // 這是一個(gè)真正的 C 風(fēng)格數(shù)組,大小在編譯時(shí)就固定為 N
        // 成員函數(shù)
        constexpr std::size_t size() const noexcept { // size() 直接返回編譯時(shí)常量 N
            return N;
        }
        T& operator[](std::size_t index); // 訪問(wèn)元素,當(dāng)然還是 T 類型
        const T& operator[](std::size_t index) const;
        // ... 迭代器、fill、swap 等 ...
        // 很多操作可能在內(nèi)部利用 N 進(jìn)行編譯時(shí)優(yōu)化,比如循環(huán)展開(kāi)
    };
}

看看這里的 std::size_t N:

  • std::size_t: 這是非類型參數(shù)的類型,它指定了 N 必須是一個(gè)無(wú)符號(hào)整數(shù),通常用來(lái)表示大小或索引。
  • N: 這是非類型參數(shù)的名字。

如何使用:當(dāng)你寫(xiě) std::array<int, 10> 時(shí),T 被替換為 int,N 被替換為常量值 10。編譯器會(huì)生成一個(gè)特定的類,其內(nèi)部有一個(gè) int _elements[10] 的成員。如果你寫(xiě) std::array<double, 100>,T 是 double,N 是 100,生成類的內(nèi)部就是 double _elements[100]。

好處:

  • 性能:因?yàn)榇笮?N 是編譯時(shí)常量,std::array 通常可以直接在棧上分配內(nèi)存(如果大小合適),避免了堆分配的開(kāi)銷(xiāo)。它的 size() 方法是 constexpr,意味著可以在編譯時(shí)獲取大小,編譯器可以基于這個(gè)固定大小進(jìn)行各種優(yōu)化(比如循環(huán)展開(kāi))。
  • 類型安全:std::array<int, 10> 和 std::array<int, 11> 是完全不同的類型!這可以在編譯時(shí)捕捉到很多錯(cuò)誤,比如你試圖將一個(gè)大小為 10 的數(shù)組賦值給大小為 11 的數(shù)組。

實(shí)戰(zhàn)演練 2:std::get 從元組中取元素

std::tuple 允許你將不同類型的元素聚合在一起。那怎么在編譯時(shí)取出特定位置的元素呢?答案還是非類型參數(shù)!

#include <tuple>
#include <string>
#include <iostream>
int main() {
    std::tuple<int, double, std::string> myTuple(10, 3.14, "Hello");
    // 使用 std::get<I>(tuple)
    int i = std::get<0>(myTuple);       // 獲取第 0 個(gè)元素 (int),這里的 0 就是非類型參數(shù)
    double d = std::get<1>(myTuple);    // 獲取第 1 個(gè)元素 (double),這里的 1 是非類型參數(shù)
    std::string s = std::get<2>(myTuple); // 獲取第 2 個(gè)元素 (string),這里的 2 是非類型參數(shù)
    std::cout << i << ", " << d << ", " << s << std::endl;
    // std::get<3>(myTuple); // 編譯錯(cuò)誤!索引越界,編譯器在編譯時(shí)就能發(fā)現(xiàn)
    return 0;
}

std::get 函數(shù)模板大概長(zhǎng)這樣(概念上的):

namespace std {
    // 通過(guò)索引獲取元素
    template <std::size_t I, typename... Types> // I 是非類型參數(shù) (索引),Types... 是類型參數(shù)包
    /* 返回類型依賴于 I 和 Types... */ 
    get(tuple<Types...>& t) noexcept;
    // 還有 const&, &&, const&& 的重載版本
}

這里的 std::size_t I 就是一個(gè)非類型參數(shù)。你傳遞一個(gè)編譯時(shí)常量整數(shù)(如 0, 1, 2)給它,std::get 就能在編譯時(shí)知道你要訪問(wèn)元組中的哪個(gè)元素,并返回對(duì)應(yīng)類型的引用。這比運(yùn)行時(shí)通過(guò)索引訪問(wèn)(如果可以的話)要快得多,而且更安全,因?yàn)闊o(wú)效的索引會(huì)在編譯階段就被拒絕。

小結(jié)非類型參數(shù):

  • 它是模板的"規(guī)格",決定了模板實(shí)例化的"尺寸"、"編號(hào)"或"特定配置值"。
  • 聲明時(shí)需要指定參數(shù)的類型(通常是整型、指針、引用等)。
  • 傳遞的是編譯時(shí)常量值。
  • 常用于定義固定大小(如 std::array)、指定索引(如 std::get)。
  • 可以增強(qiáng)類型安全(不同值的模板實(shí)例是不同類型)。

四、 類型與非類型參數(shù)的協(xié)作

圖片

很多強(qiáng)大的模板會(huì)同時(shí)使用類型參數(shù)和非類型參數(shù),std::array<T, N> 就是最經(jīng)典的例子。T 決定了數(shù)組元素的“材質(zhì)”,N 決定了數(shù)組的“大小”。兩者結(jié)合,創(chuàng)造出一個(gè)既泛型(適用于多種類型)又高效(固定大小,編譯時(shí)優(yōu)化)的數(shù)據(jù)結(jié)構(gòu)。

再比如,你可以寫(xiě)一個(gè)模板函數(shù),打印一個(gè) std::array 的內(nèi)容:

#include <array>
#include <iostream>

template <typename T, std::size_t N> // 同時(shí)使用類型參數(shù) T 和非類型參數(shù) N
void print_array(const std::array<T, N>& arr) {
    std::cout << "[ ";
    for (std::size_t i = 0; i < N; ++i) { // 循環(huán)上限直接用 N
        std::cout << arr[i] << (i == N - 1 ? "" : ", ");
    }
    std::cout << " ]" << std::endl;
}

int main() {
    std::array<int, 5> ints = {1, 2, 3, 4, 5};
    std::array<double, 3> doubles = {1.1, 2.2, 3.3};
    std::array<char, 4> chars = {'a', 'b', 'c', 'd'};

    print_array(ints);    // 編譯器推導(dǎo)出 T=int, N=5
    print_array(doubles); // 編譯器推導(dǎo)出 T=double, N=3
    print_array(chars);   // 編譯器推導(dǎo)出 T=char, N=4

    return0;
}

這個(gè) print_array 函數(shù)因?yàn)橥瑫r(shí)接受 T 和 N 作為模板參數(shù),所以可以完美地處理任何類型、任何(固定)大小的 std::array。編譯器在調(diào)用點(diǎn)會(huì)根據(jù)傳入的 std::array 類型自動(dòng)推導(dǎo)出 T 和 N 的值。

1. C++17 和 C++20 的小升級(jí)(錦上添花)

auto 作為非類型參數(shù) (C++17):你可以讓編譯器自動(dòng)推導(dǎo)非類型參數(shù)的具體類型,只要它符合要求。

template <auto Value> // Value 的類型由傳入的常量值決定
void process_value() {
    // ... 可以使用 Value,它的類型是確定的 ...
    std::cout << "Processing value: " << Value << " of type " << typeid(decltype(Value)).name() << std::endl;
}

process_value<10>();   // Value 是 int, 值為 10
process_value<'a'>(); // Value 是 char, 值為 'a'
// process_value<3.14>(); // C++17 通常還不支持浮點(diǎn)數(shù)作為非類型參數(shù) (C++20 有條件支持)

五、 總結(jié)

  • 類型參數(shù) :用 typename 或 class 聲明,是類型的占位符。它讓模板能夠適用于不同的數(shù)據(jù)類型,是泛型容器(如 vector)和泛型算法的基礎(chǔ)。它決定了"做什么"或"用什么材質(zhì)"。
  • 非類型參數(shù) :聲明時(shí)帶有具體類型(如 int, size_t, 指針,C++17 auto 等),是編譯時(shí)常量的占位符。它讓模板能夠根據(jù)編譯時(shí)確定的值進(jìn)行定制,常用于固定大小(如 array)、索引(如 get)或配置。它決定了"多大尺寸"、"哪個(gè)編號(hào)"或"具體配置"。
責(zé)任編輯:趙寧寧 來(lái)源: CppPlayer
相關(guān)推薦

2010-02-04 14:01:43

C++非類型類模板參數(shù)

2010-02-04 14:22:25

C++函數(shù)模板非類型參

2021-07-08 10:08:03

DvaJS前端Dva

2020-12-07 06:19:50

監(jiān)控前端用戶

2021-06-30 08:45:02

內(nèi)存管理面試

2020-03-18 14:00:47

MySQL分區(qū)數(shù)據(jù)庫(kù)

2022-06-07 10:13:22

前端沙箱對(duì)象

2023-11-23 13:39:17

2021-11-20 10:27:43

Python數(shù)據(jù)類型

2021-11-22 06:21:31

Python數(shù)據(jù)類型Python基礎(chǔ)

2019-11-06 17:30:57

cookiesessionWeb

2021-08-05 06:54:05

觀察者訂閱設(shè)計(jì)

2022-04-11 10:56:43

線程安全

2021-01-13 05:21:59

參數(shù)

2019-06-29 15:32:43

計(jì)算機(jī)互聯(lián)網(wǎng) 技術(shù)

2024-08-08 14:57:32

2020-12-21 07:54:46

CountDownLa用法源碼

2021-10-20 08:49:30

Vuexvue.js狀態(tài)管理模式

2023-11-23 06:50:08

括號(hào)

2020-12-18 09:36:01

JSONP跨域面試官
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 亚洲444eee在线观看 | 干干干操操操 | 国产激情91久久精品导航 | 国产一区二区三区久久久久久久久 | 亚洲国产精品成人综合久久久 | 一区二区在线不卡 | 国产成人精品综合 | 国产欧美精品 | 国产高清久久久 | 久久久久亚洲精品国产 | 亚洲一区二区在线 | 一区二区三区精品在线 | 精品久久久久一区二区国产 | 免费亚洲一区二区 | 日韩欧美高清 | 一级欧美一级日韩片免费观看 | 国产激情一区二区三区 | 国产精品高清在线 | 国产午夜影院 | 91看片免费版 | 欧美一区二区三区一在线观看 | 成人在线免费观看 | 成人av一区二区三区 | 久久久精| 国产欧美日韩一区二区三区 | 欧美日韩精品专区 | 手机av免费在线 | 免费成人在线网站 | www.五月天婷婷.com | 9久9久 | 久久久久国产精品午夜一区 | 国产精品色av | 欧美日韩久久 | 亚洲精品无 | 日韩精品视频在线播放 | 国产成人精品免费视频大全最热 | 亚洲精品一区在线观看 | 祝你幸福电影在线观看 | 91麻豆精品国产91久久久久久 | 在线精品一区 | 亚洲国产精品99久久久久久久久 |