国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

這篇具有很好參考價值的文章主要介紹了【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點(diǎn)擊"舉報違法"按鈕提交疑問。

一、非類型模板參數(shù)

模板參數(shù)分為類型形參非類型形參,其中,類型形參即出現(xiàn)在模板參數(shù)列表中,跟在class或者typename之類的參數(shù)類型名稱,非類型形參,就是用一個常量作為類(函數(shù))模板的一個參數(shù),在類(函數(shù))模板中可將該參數(shù)當(dāng)成常量來使用

我們以定義一個靜態(tài)的數(shù)組為例,在沒有非類型模板參數(shù)的時候,我們只能采用如下的方式來定義一個靜態(tài)的數(shù)組:

#define N 10
template<class T>
class arr
{
public:
	// ...
private:
	T _a[N];
};
void test()
{
    arr<int> arr;
}

但是這樣定義一個數(shù)組有一個極大的缺陷,那就是當(dāng)我們同時需要使用不同大小的數(shù)組的時候,就無法實(shí)現(xiàn),針對這個問題,C++設(shè)計出了非類型模板參數(shù)來解決這個問題,非類型模板參數(shù)和類型模板參數(shù)一樣,也是通過傳遞不同的非類型模板參數(shù)來定義不同的類,代碼如下:

template<class T,size_t N>
class arr
{
public:
	// ...
private:
	T _a[N];
};
void test()
{
	arr<int, 10> arr1;
	arr<int, 100> arr2;
}

【注意】

1.非類型模板參數(shù)可以給缺省值

2.浮點(diǎn)數(shù)、類對象以及字符串是不允許作為非類型模板參數(shù)的,非類型模板參數(shù)只能是整形

3.非類型的模板參數(shù)必須在編譯期就能確認(rèn)結(jié)果,即非類型模板參數(shù)的實(shí)參只能是一個常量

二、模板的特化

1.模板特化的概念

通常情況下,使用模板可以實(shí)現(xiàn)一些與類型無關(guān)的代碼,但對于一些特殊類型的可能會得到一些錯誤的結(jié)果,需要特殊處理,比如:實(shí)現(xiàn)了一個專門用來進(jìn)行小于比較的函數(shù)模板

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

// 函數(shù)模板 -- 參數(shù)匹配
template<class T>
bool Less(T left, T right)
{
	return left < right;
}

int main()
{
	cout << Less(1, 2) << endl; // 可以比較,結(jié)果正確
	Date d1(2022, 7, 7);
	Date d2(2022, 7, 8);
	cout << Less(d1, d2) << endl; // 可以比較,結(jié)果正確
	Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 可以比較,結(jié)果錯誤
	return 0;
}

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

我們可以看到,Less在絕大多數(shù)的情況下都可以進(jìn)行正常的比較,但是在特定的場景下就可能得到錯誤的結(jié)果,比如在上訴案例中p1指向的d1明顯小于p2指向的d2對象,但是Less內(nèi)部并沒有比較p1和p2指向?qū)ο蟮膬?nèi)容,而比較的是p1和p2的地址,從而使得得到的不是我們預(yù)期的結(jié)果

為了解決上面的問題,我們就需要對模板進(jìn)行特化,即在原模板類的基礎(chǔ)上,針對特殊類型進(jìn)行特殊化處理,模板特化中分為函數(shù)模板特化和類模板特化

2.函數(shù)模板的特化

函數(shù)模板的特化步驟:

1.必須要先有一個基礎(chǔ)的函數(shù)模板

2.關(guān)鍵字template后面接一對空的尖括號<>

3.函數(shù)名后跟一對尖括號,尖括號中指定需要特化的類型

4.函數(shù)形參表: 必須要和模板函數(shù)的基礎(chǔ)參數(shù)類型完全相同,如果不同編譯器可能會報一些奇怪的錯誤

// 函數(shù)模板 -- 參數(shù)匹配
template<class T>
bool Less(T left, T right)
{
	return left < right;
}
// 對Less函數(shù)模板進(jìn)行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

我們可能會說,我們?yōu)槭裁床恢苯又剌d一個Date*的函數(shù)呢,而是大費(fèi)周章采用模板特化呢,確實(shí)是這樣的,因?yàn)楹瘮?shù)支持多個重載函數(shù),所以注意:一般情況下如果函數(shù)模板遇到不能處理或者處理有誤的類型,為了實(shí)現(xiàn)簡單通常都是將該函數(shù)直接給出

bool Less(Date* left, Date* right)
{
 return *left < *right;
}

函數(shù)重載實(shí)現(xiàn)簡單明了,代碼的可讀性高,容易書寫,因?yàn)閷τ谝恍﹨?shù)類型復(fù)雜的函數(shù)模板,特化時特別給出,因此函數(shù)模板不建議特化

3.類模板的特化

3.1 全特化

全特化即是將模板參數(shù)列表中所有的參數(shù)都確定化。

template<class T1, class T2>
class Data
{
public:
	Data()
    {
        cout << "Data<T1, T2>" << endl;
    }
private:
	T1 _d1;
	T2 _d2;
};
template<>
class Data<int, char>
{
public:
	Data()
    {
        cout << "Data<int, char>" << endl;
    }
private:
	int _d1;
	char _d2;
};
void TestVector()
{
	Data<int, int> d1;
	Data<int, char> d2;
}

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

3.2 偏特化

偏特化:任何針對模版參數(shù)進(jìn)一步進(jìn)行條件限制設(shè)計的特化版本。比如對于以下模板類

template<class T1, class T2>
class Data
{
public:
    Data()
    {
        cout<<"Data<T1, T2>" <<endl;
    }
private:
    T1 _d1;
    T2 _d2;
};

偏特化有以下兩種表現(xiàn)方式:

1.部分特化

將模板參數(shù)類表中的一部分參數(shù)特化

// 將第二個參數(shù)特化為int
template <class T1>
class Data<T1, int>
{
public:
    Data()
    {
        cout<<"Data<T1, int>" <<endl;
    }
private:
    T1 _d1;
    int _d2;
};
void TestVector()
{
    // 第二個參數(shù)與模板特化中的特化參數(shù)相同,優(yōu)先使用特化模板進(jìn)行實(shí)例化
	Data<int, int> d1;
    // 使用普通模板實(shí)例化
	Data<int, char> d2;
}

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

我們可以看到,我們將模板的部分參數(shù)顯示指定為某種具體的類型,這樣模板參數(shù)進(jìn)行匹配的時候會優(yōu)先匹配

2.參數(shù)更進(jìn)一步的限制

偏特化并不僅僅是指特化部分參數(shù),而是針對模板參數(shù)更進(jìn)一步的條件限制所設(shè)計出來的一個特化版本

//兩個參數(shù)偏特化為指針類型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data()
	{
		cout << "Data<T1*, T2*>" << endl;
	}

private:
	T1 _d1;
	T2 _d2;
};
//兩個參數(shù)偏特化為引用類型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}

private:
	const T1& _d1;
	const T2& _d2;
};
void test()
{
	Data<double, int> d1; // 調(diào)用特化的int版本
	Data<int, double> d2; // 調(diào)用基礎(chǔ)的模板 
	Data<int*, int*> d3; // 調(diào)用特化的指針版本
	Data<int&, int&> d4(1, 2); // 調(diào)用特化的指針版本
}

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

從上面的結(jié)果,我們可以看到,通過偏特化對模板參數(shù)進(jìn)行進(jìn)一步限制,比如將模板參數(shù)定義為<T*,T*>,這樣不管是任何類型的指針都會調(diào)用該特化模板,從而實(shí)現(xiàn)了在限制參數(shù)類型的同時又不會將參數(shù)局限為某一具體類型

4.類模板特化應(yīng)用示例

有如下專門用來按照小于比較的類模板Less:

#include<vector>
#include <algorithm>
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};
template<class T>
struct Less
{
	bool operator()(const T& x, const T& y) const
	{
		return x < y;
	}
};
void test()
{
	Date d1(2023, 4, 3);
	Date d2(2023, 4, 1);
	Date d3(2023, 4, 2);
	vector<Date> v1;
	v1.push_back(d1);
	v1.push_back(d2);
	v1.push_back(d3);
	// 可以直接排序,結(jié)果是日期升序
	sort(v1.begin(), v1.end(), Less<Date>());
	for (auto e : v1)
	{
		cout << e << endl;
	}
	cout << endl;
	vector<Date*> v2;
	v2.push_back(&d1);
	v2.push_back(&d2);
	v2.push_back(&d3);

	// 可以直接排序,結(jié)果錯誤日期還不是升序,而v2中放的地址是升序
	// 此處需要在排序過程中,讓sort比較v2中存放地址指向的日期對象
	// 但是走Less模板,sort在排序時實(shí)際比較的是v2中指針的地址,因此無法達(dá)到預(yù)期
	sort(v2.begin(), v2.end(), Less<Date*>());
	for (auto e : v2)
	{
		cout << *e << endl;
	}
	cout << endl;
}

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

通過觀察上述程序的結(jié)果發(fā)現(xiàn),對于日期對象可以直接排序,并且結(jié)果是正確的。但是如果待排序元素是指針,結(jié)果就不一定正確。因?yàn)椋簊ort最終按照Less模板中方式比較,所以只會比較指針,而不是比較指針指向空間中內(nèi)容,此時可以使用類版本特化來處理上述問題:

// 對Less類模板按照指針方式特化
template<>
struct Less<Date*>
{
    bool operator()(Date* x, Date* y) const
    {
        return *x < *y;
    }
};

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

三、模板的分離編譯

1.什么是分離編譯

一個程序(項目)由若干個源文件共同實(shí)現(xiàn),而每個源文件單獨(dú)編譯生成目標(biāo)文件,最后將所有目標(biāo)文件鏈接起來形成單一的可執(zhí)行文件的過程稱為分離編譯模式

2.模板的分離編譯

我們以stack為例:

stack.h

#pragma once
#include<iostream>
using namespace std;

template<typename T>
class Stack
{
public:
	Stack(int capacity = 4);
	~Stack();
	void Push(const T& x);

private:
	T* _a;
	int _top;
	int _capacity;
};

stack.cpp

#include "Stack.h"

template<class T>
Stack<T>::Stack(int capacity)
{
	cout << "Stack(int capacity = )" << capacity << endl;

	_a = (T*)malloc(sizeof(T)*capacity);
	if (_a == nullptr)
	{
		perror("malloc fail");
		exit(-1);
	}

	_top = 0;
	_capacity = capacity;
}

template<class T>
Stack<T>::~Stack()
{
	cout << "~Stack()" << endl;

	free(_a);
	_a = nullptr;
	_top = _capacity = 0;
}

template<class T>
void Stack<T>::Push(const T& x)
{
	// ....
	// 
	_a[_top++] = x;
}

test.cpp

#include "stack.h"

int main()
{
	Stack<int> st;
	st.Push(1);
	st.Push(2);
	st.Push(3);
	return 0;

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

當(dāng)我們運(yùn)行的時候發(fā)現(xiàn)出現(xiàn)了鏈接性錯誤;造成這種的原因如下:

我們知道,一個.c/.cpp程序變成.exe可執(zhí)行程序一共需要經(jīng)歷四個步驟,分別是預(yù)處理,編譯,匯編和鏈接,在這個過程中,他們所執(zhí)行的工作為:

預(yù)處理:注釋的刪除,#define定義的符號,宏的替換以及刪除,各種條件編譯的處理,頭文件的展開

編譯:進(jìn)行詞法分析,語法分析,語義分析和符號匯總

匯編:生成符號表

鏈接:合并段表,符號表的合并和重定位

此外,預(yù)處理,編譯,匯編這幾個階段每個源(.c文件)文件都是獨(dú)立進(jìn)行的,只有在鏈接的時候才會將這幾個目標(biāo)文件合并到一起形成可執(zhí)行程序

【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯

綜上所訴,我們可知程序報錯的原因如下:

1.預(yù)處理時,stack.h頭文件分別在stack.c和test.c源文件展開

2.經(jīng)過編譯,stack.cpp和test.cpp分別轉(zhuǎn)變成匯編代碼

3.經(jīng)過匯編,stack.cpp里面有stack,push函數(shù)的聲明,但是沒有他們的定義,所以test.cpp在生成符號表的時候會給這些函數(shù)一個無效的地址,此外,由于stack.cpp里面沒有對函數(shù)模板實(shí)例化的代碼,即沒有stack,也就沒有生成具體的代碼,即沒有stack的定義,所以stack.cpp的符號表里函數(shù)對應(yīng)的地址是一個無效的地址

4.在鏈接時,需要將test.cpp和stack.cpp符號表的內(nèi)容進(jìn)行合并與重定位,但由于他們符號表中都是無效的地址,所以會發(fā)生鏈接錯誤

我們了解程序報錯的原因之后,想到的第一個解決方案應(yīng)該是在stack.cpp中對模板進(jìn)行實(shí)例化

// 在stack.cpp中增加對stack的顯式實(shí)例化
template class Stack<int>;

這樣的做法確實(shí)能夠解決這個問題,但是當(dāng)我們定義一個存放double類型的棧的時候,此時,我們又需要在stack.cpp中對stack再進(jìn)行實(shí)例化一次,如果我們再定義不同的對象,就需要不斷的進(jìn)行顯式實(shí)例化,也就是說,在同一份代碼中我們只能定義同一類類型的對象,那么這樣就十分麻煩,也失去了模板的初衷了,所以模板不支持分離編譯,我們一般采用如下的方法解決:

1.將聲明和定義放到一個文件"xxx.hpp" 里面或者xxx.h**其實(shí)也是可以的

2.模板定義的位置顯式實(shí)例化。這種方法不實(shí)用,不推薦使用

【分離編譯擴(kuò)展閱讀】為什么C++編譯器不能支持對模板的分離式編譯

但是聲明解決的兩種方式有一個問題,就是將函數(shù)的聲明和定義放在同一個文件中,我們就將類提供給別人使用時,也將底層實(shí)現(xiàn)也暴露了給別人

四、模板總結(jié)

模板優(yōu)點(diǎn)

1.模板復(fù)用了代碼,節(jié)省資源,更快的迭代開發(fā),C++的標(biāo)準(zhǔn)模板庫(STL)因此而產(chǎn)生

2.增強(qiáng)了代碼的靈活性

模板缺點(diǎn)

1.模板會導(dǎo)致代碼膨脹問題,也會導(dǎo)致編譯時間變長

2.出現(xiàn)模板編譯錯誤時,錯誤信息非常凌亂,不易定位錯誤文章來源地址http://www.zghlxwxcb.cn/news/detail-422678.html

到了這里,關(guān)于【C++】模板進(jìn)階--非類型模板參數(shù)&&模板特化及分離編譯的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請點(diǎn)擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【C++初階(十)】C++模板(進(jìn)階) ---非類型模板參數(shù)、模板的特化以及模板的分離編譯

    【C++初階(十)】C++模板(進(jìn)階) ---非類型模板參數(shù)、模板的特化以及模板的分離編譯

    本專欄內(nèi)容為:C++學(xué)習(xí)專欄,分為初階和進(jìn)階兩部分。 通過本專欄的深入學(xué)習(xí),你可以了解并掌握C++。 ??博主csdn個人主頁:小小unicorn ?專欄分類:C++ ??代碼倉庫:小小unicorn的代碼倉庫?? ??????關(guān)注我?guī)銓W(xué)習(xí)編程知識 模板參數(shù)可分為類型形參和非類型形參。 類型形

    2024年01月18日
    瀏覽(18)
  • 【C++干貨鋪】非類型模板 | 模板特化 | 模板分離編譯

    【C++干貨鋪】非類型模板 | 模板特化 | 模板分離編譯

    ========================================================================= 個人主頁點(diǎn)擊直達(dá): 小白不是程序媛 C++系列專欄: C++干貨鋪 代碼倉庫: Gitee ========================================================================= 目錄 非類型模板參數(shù) 模板的特化 什么是模板特化? 函數(shù)模板特化 類模板的特化 全

    2024年02月04日
    瀏覽(23)
  • C#模擬C++模板特化對類型的值的支持

    C#模擬C++模板特化對類型的值的支持

    C++的模板相比于C#的泛型,有很多地方都更加的靈活(雖然代價是降低了編譯速度),比如C++支持變長參數(shù)模板、支持枚舉、int等類型的值作為模板參數(shù)。 C++支持枚舉、int等類型的值作為模板參數(shù),為C++的靜態(tài)多態(tài)編程提供了很好的幫助,比如根據(jù)枚舉值編譯期確定某個對象

    2023年04月17日
    瀏覽(24)
  • C++ - 模板分離編譯

    C++ - 模板分離編譯

    ?我們先來看一個問題,我們用 stack 容器的聲明定義分離的例子來引出這個問題: // stack.h 現(xiàn)有如上這個stack 類,我們把 push ()函數(shù) 和 pop()函數(shù)聲明定義分離,如下所示: ?// stack.cpp 看似上述的分離是沒有問題,但是,當(dāng)我們編譯的時候就報錯了: ?上述報了一些 li

    2024年02月14日
    瀏覽(19)
  • (C++)模板分離編譯面對的問題

    (C++)模板分離編譯面對的問題

    一個程序(項目)由若干個源文件共同實(shí)現(xiàn),而每個源文件單獨(dú)編譯生成目標(biāo)文件,最后將所有目標(biāo)文件鏈接起來形成單一的可執(zhí)行文件的過程稱為分離編譯模式。 假如有以下場景,模板的聲明與定義分離開,在頭文件中進(jìn)行聲明,源文件中完成定義: 如果模板進(jìn)行分離編

    2023年04月08日
    瀏覽(33)
  • 【C++】模板+模板特化

    【C++】模板+模板特化

    鐵汁們,今天給大家分享一篇模板+模板特化,來吧,開造?? 定義:編寫跟具體類型無關(guān)的通用代碼,是實(shí)現(xiàn)代碼復(fù)用的一種手段。模板是泛型編程的基礎(chǔ)。 問:如何實(shí)現(xiàn)一個通用的swap函數(shù)? 答:寫成函數(shù)模板,不能在函數(shù)重載了。原因:代碼復(fù)用率低,重載的函數(shù)僅是

    2024年03月24日
    瀏覽(21)
  • C++類模板的特化(三)

    本文主要介紹類模板的特化、局部特化和缺省模板實(shí)參; 類模板的特化(Class Template Specialization)是指為特定的模板參數(shù)提供自定義實(shí)現(xiàn)的過程。通過特化,我們可以針對某些特定的類型或條件提供不同的行為或?qū)崿F(xiàn); 如果需要特化一個類模板,需要特化該模板中的所有成員

    2024年02月11日
    瀏覽(17)
  • C++函數(shù)模板、特例化、非類型參數(shù)、類模板、allocator

    模板對類型能進(jìn)行參數(shù)化成【模板參數(shù)】,輸入的是類型,生成的是代碼。使用的時候,每指定一份類型,模板就會根據(jù)類型生成一份新的代碼(比如函數(shù)模板實(shí)例化生成的是【模板函數(shù)】),有利于減少代碼量,通過較少的代碼也能實(shí)現(xiàn)函數(shù)重載。 調(diào)用函數(shù)模板的時候,一

    2024年02月21日
    瀏覽(15)
  • 【C++進(jìn)階】C++11(下)可變參數(shù)模板&lambda表達(dá)式&包裝器

    【C++進(jìn)階】C++11(下)可變參數(shù)模板&lambda表達(dá)式&包裝器

    我們緊接著上一節(jié)的講解來進(jìn)行 C++11的新特性可變參數(shù)模板能夠讓您創(chuàng)建可以接受可變參數(shù)的函數(shù)模板和類模板,相比C++98/03,類模版和函數(shù)模版中只能含固定數(shù)量的模版參數(shù),可變模版參數(shù)無疑是一個巨大的改進(jìn)。然而由于可變模版參數(shù)比較抽象,使用起來需要一定的技巧

    2024年04月11日
    瀏覽(31)
  • easyx常見問題:編譯錯誤:“2 個重載中沒有一個可以轉(zhuǎn)換所有參數(shù)類型” ;編譯錯誤:“EasyX is only for C++”

    錯誤描述: 一些程序在 VC6 下運(yùn)行好好地,但是放到 VC2008 及更高版本 VC 下編譯卻報錯誤(以下僅以 VC2008 舉例,高版本 VC 類似),例如使用如下語句: 在 VC6 下可以成功編譯,但在 VC2008 下編譯后會有錯誤。 錯誤提示如下: 同樣的,對于其他一些包含字符串調(diào)用的函數(shù),例

    2024年02月05日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包