1. 程式人生 > >[C++ Template]深入模板--特化與過載

[C++ Template]深入模板--特化與過載

目錄

第12章 特化與過載

目前為止,我們已經知道了:C++模板如何使一個泛型定義擴充套件成一些相關的類家族或者函式家族。雖然這是一個功能很強大的機制,但該機制並非適合於所有的情況;在一些情況下,這種泛型操作就不是特定模板引數替換的最佳選擇。

12.2 過載函式模板

兩個同名的函式模板可以同時存在,還可以對它們進行例項化,使它們具有相同的引數型別。下面是另一個簡單的例子:

template<typename T>
int f(T)
{
    return 1;
}
template<typename T>
int f(T*)
{
    return 2;
}

如果我們用int*來替換第1個模板的T,用int來替換第2個模板的T,那麼將會獲得兩個具有相同引數型別(和返回型別)的同名函式。也就是說,不僅是同名模板可以同時存在,它們各自的例項化體也可以同時存在,即使這些例項化體具有相同的引數型別和返回型別。

12.2.1 簽名

只要具有不同的簽名,兩個函式就可以在同一個程式中同時存在。我們對函式的簽名定如下

1.非受限函式的名稱(或者產生自函式模板的這類名稱)。

2.函式名稱所屬的類作用域或者名字空間作用域;如果函式名稱是具有內部連結的,還包括該名稱宣告所在的翻譯單元。\

3.函式的const、volatile或者const volatile限定符(前提是它是一個具有這類限定符的成員函式)。

4.函式引數的型別(如果這個函式是產生自函式模板的,那麼指的是模板引數被替換之前的型別)。

5.如果這個函式是產生自函式模板,那麼包括它的返回型別。

6.如果這個函式是產生自函式模板,那麼包括模板引數和模板實參。

這就意味著:從原則上講,下面的模板和它們的例項化體可以在同個程式中同時存在:

template<typename T1, typename T2>
void f1(T1, T2);
template<typename T1, typename T2>
void f1(T2, T1);
template<typename T>
long f2(T);
template<typename T>
char f2(T);

然而,如果上面這些模板是在同一個作用域中進行宣告的話,我們可能不能使用某些模板,因為例項化過程可能會導致過載二義性。例如:

template<typename T1, typename T2>
void f1(T1, T2)
{
    std::cout << "f1(T1, T2)\n";
}

template<typename T1, typename T2>
void f1(T2, T1)
{
    std::cout << "f1(T2, T1)\n";
}
// 到這裡為止一切都是正確的

int main()
{
    f1<char, char>('a', 'b'); // 錯誤:二義性
}

在上面的程式碼中,雖然函式f1<T1 = char, T2 = char>(T1,T2)可以和函式f1<T1 = char, T2 =char>(T2, T1)同時存在,但是過載解析規則將不知道應該選擇哪一個函式。

12.2.2 過載的函式模板的區域性排序

template<typename T>
int f(T)
{
    return 1;
}

template<typename T>
int f(T*)
{
    return 2;
}

int main()
{
    std::cout << f(0) << std::endl;
    std::cout << f((int*)0) << std::endl;
}

讓我們先考慮呼叫(f(0)):實參的型別是int,如果用int替換T,就能和第1個模板的引數匹配。然而,第2個模板的引數型別總是一個指標;因此,經過演繹之後,只有產生自第1個模板的例項才是該呼叫的候選函式。在這個呼叫中,過載解析並沒有發揮作用。

第2個呼叫((f ( (int*) 0) )就顯得比較有趣:對於這兩個模板,實參演繹都可以獲得成功,於是就獲得兩個函式,即f<int*>(int*)和f<int>(int*)。如果根據原來的過載解析觀點,這兩個函式和實參型別為 int*的呼叫的匹配程度是一樣的,這也就意味著該呼叫是二義性的(見附錄B)。然而,在這種情況下,還應該考慮過載解析的額外規則:選擇“產生自更特殊的模板的函式”。因此(我們將在後面的小節看到),第2個模板被認為是更加特殊的模板,從而(再次)產生下面的輸出結果:

1 
2


12.2.3 正式的排序原則

接下來,我們將給出一個精確的過程,它能夠判斷:在參與過載集的所有函式模板中,某個函式模板是否比另一個函式模板更加特殊。然而,我們應該知道這只是不完整的排序原則:就是說,兩個模板也可能會被認為具有相同的特殊程度。如果過載解析必須在這兩個特殊程度相同的模板中進行選擇,那麼將不能做出任何決定,也就是說程式包含了一個二義性錯誤。

假設我們要比較兩個同名的函式模板ft1和ft2,對於給定的函式呼叫,它們看起來都是可行的。在我們下面的討論中,對於沒有被使用的預設函式實參和省略號引數,我們將不考慮。接下來,通過如下替換模板引數,我們將為這兩個模板虛構兩份不同的實參型別(如果是轉型函式模板,那麼還包括返回型別)列表,其中第1份列表針對第1個模板,第2份列表針對第2個模板。“虛構”的實參列表將這樣地替換每個模板引數:

1.用唯一的“虛構”型別替換每個模板型別引數。

2.用唯一的“虛構”類模板替換每個模板的模板引數。

3.用唯一的適當型別的“虛構”值替換每個非型別引數。

如果第 2 個模板針對第 1 份列表可以進行成功的實參演繹(能夠進行精確的匹配),而第1個模板針對第2份列表的實參演繹以失敗告終,那麼我們就稱第1個模板要比第2個模板更加特殊。反之,如果第1個模板針對第2份列表可以進行成功的實參演繹(能夠進行精確的匹配),而第2個模板針對第1份列表的實參演繹失敗,那麼我們就稱第2個模板要比第1個模板更加特殊。否則的話(或者是兩個都不能成功演繹,或者是兩個都能成功演繹),我們就稱這兩個模板之間不存在特殊的排序關係。

讓我們把這個過程應用於前面的例子,來更加清楚地闡明上面的問題。根據這兩個模板和前面所描述的模板引數替換方法,我們虛構了兩個實參型別列表:(A1)和(A2*)(A1和A2是不同的虛構型別)。顯然,第1個模板可以成功地演繹第2份實參列表,只要用A2* 替換T就可以。然而,第2個模板卻不能成功地演繹第1份列表,因為第2個模板的T*是不能和非指標型別A1進行匹配的。因此,我們就可以(正式地)得出結論:第2個模板比第1個模板更加特殊。

12.2.4 模板和非模板

函式模板也可以和非模板函式同時過載。當其它的所有條件都是一樣的時候,實際的函式呼叫將會優先選擇非模板函式。

12.3 顯式特化

具有對函式模板進行過載的這種能力,再加上可以利用區域性排序規則選擇最佳匹配的函式模板,我們就能夠給泛型實現新增更加特殊的模板,從而可以透明地獲得具有更高效率的程式碼。然而,類模板是不能被過載的;但我們可以選擇另一種替換的機制來實現這種透明自定義類模板的能力,那就是顯式特化。

12.3.1 全域性的類模板特化

引入全域性特化需要用到下面3個標記序列:template、< 和 > 。另外,緊跟在類名稱聲明後面的就是要進行特化的模板實參。下面的例子說明了這一點:

template<typename T>
class S 
{
public:
    void info() 
         {
            std::cout << "generic (S<T>::info())\n";
         }
};

template<>
class S<void> 
{
public:
    void msg() 
         {
            std::cout << "fully specialized (S<void>::msg())\n";
         }
}

我們看到,全域性特化的實現並不需要與(原來的)泛型實現有任何關聯,這就允許我們可以包含不同名稱的成員函式(info相對msg)。實際上,全域性特化只和類模板的名稱有關聯。

另外,指定的模板實參列表必須和相應的模板引數列表一一對應。例如,我們不能用一個非型別值來替換一個模板型別引數。然而,如果模板引數具有預設模板實參,那麼用來替換的模板實參就是可選的(即不是必須的):

template<typename T>
class Types 
{
public:
    typedef int I;
};

template<typename T, typename U = typename Types<T>::I>
class S;           // (1)

template<> class S<void>  // (2)
{       
public:
    void f();
};
template<> class S<char, char>; // (3)
template<> class S<char, 0>;  // 錯誤:不能用0來替換U

int main()
{
    S<int>*   pi;  // 正確:使用(1),這裡不需要定義
    S<int>   e1;  // 錯誤:使用(1),需要定義,但找不到定義
    S<void>*  pv;  // 正確:使用(2)
    S<void,int> sv;  // 正確:使用(2),這裡定義是存在的
    S<void,char> e2;  // 錯誤:使用(1),需要定義,但找不到定義
    S<char,char> e3;  // 錯誤:使用(3),需要定義,但找不到定義
}

template<>
class S<char, char> // (3)處的定義
{};

如例子中所示,(模板)全域性特化的宣告並不一定是定義。另外,當一個全域性特化宣告之後,針對該(特化的)模板實參列表的呼叫,將不再使用模板的泛型定義,而是使用這個全域性特化的定義。因此,如果在呼叫處需要該特化的定義,而在這之前並沒有提供這個定義,那麼程式將會出現錯誤。對於類模板特化而言,“前置宣告”型別有時候是很有用的,因為這樣就可以構造相互依賴的型別。另外,以這種方式獲得的全域性特化宣告(應該記住它並不是模板宣告)和普通的類宣告是類似的,唯一的區別在於語法以及該特化的宣告必須匹配前面的模板宣告。

對於特化宣告而言,因為它並不是模板宣告,所以應該使用(位於類外部)的普通成員定義語法,來定義全域性類模板特化的成員(也就是說,不能指定template<>字首):

template<typename T>
class S;

template<> class S<char**> 
{
public:
    void print() const;
};

//下面的定義不能使用template<>字首
void S<char**>::print() const
{
    std::cout << "pointer to pointer to char\n";
}

我們可以使用一個更復雜的例子來進一步理解這個概念:

template<typename T>
class Outside 
{
public:
    template<typename U>
    class Inside {
    };
};

template<>
class Outside<void> 
{
// 下面的巢狀類和前面定義的泛型模板之間並不存在聯絡
    template<typename U>
    class Inside
    {
    private:
        static int count;
    };
};

//下面的定義不能使用template<>字首
template<typename U>
int Outside<void>::Inside<U>::count = 1;

可以用全域性模板特化來代替對應泛型模板的某個例項化體。然而,全域性模板特化和由模板生成的例項化版本是不能夠共存於同一個程式中的。如果試圖在同一個檔案中使用這兩者的話,那麼通常都會導致一個編譯期錯誤

template <typename T>
class Invalid {
};

Invalid<double> x1;  // 產生一個Invalid<double>例項化體

template<>
class Invalid<double>; // 錯誤:Invalid<double>已經被例項化了

12.3.2 全域性的函式模板特化

就語法及其後所蘊涵的原則而言,(顯式的)全域性函式模板特化和類模板特化大體上是一致的,唯一的區別在於:函式模板特化引入了過載和實參演繹這兩個概念。

如果可以藉助實參演繹(用實參型別來演繹宣告中給出的引數型別)來確定模板的特殊化版本,那麼全域性特化就可以不宣告顯式的模板實參。讓我們考慮下面的例子:

template<typename T>
int f(T)       // (1)
{
    return 1;
}

template<typename T>
int f(T*)       // (2)
{
    return 2;
}

template<> int f(int)  // OK: (1)的特化
{
    return 3;
}

template<> int f(int*) // OK: (2)的特化。
{
    return 4;
}

全域性函式模板特化不能包含預設的實參值。然而,對於基本(即要被特化的)模板所指定的任何預設實參,顯式特化版本都可以應用這些預設實參值。例如:

template<typename T>
int f(T, T x = 42)
{
    return x;
}

template<> int f(int, int = 35) // 錯誤,不能包含預設實參值
{
    return 0;
}

template<typename T>
int g(T, T x = 42)
{
    return x;
}

template<> int g(int, int y)
{
    return y/2;
}

int main()
{
    std::cout << g(0) << std::endl; // 正確,輸出21
}

12.3.3 全域性成員特化

除了成員模板之外,類模板的成員函式和普通的靜態成員變數也可以被全域性特化;實現特化的語法會要求給每個外圍類模板加上template<>字首。如果要對一個成員模板進行特化,也必須加上另一個template<>字首,來說明該宣告表示的是一個特化。為了說明這些含義,讓我們假設具有下面的宣告:

template<typename T>
class Outer 
{             // (1)
public:
    template<typename U>
    class Inner 
    {       // (2)
    private:
        static int count;       // (3)
    };

    static int code;        // (4)

    void print() const           // (5)
    {        
        std::cout << "generic";
    }
};

template<typename T>
int Outer<T>::code = 6;        // (6)

template<typename T> 
template<typename U>
int Outer<T>::Inner<U>::count = 7;  // (7)

template<> class Outer<bool> 
{          // (8)
public:
    template<typename U>
    class Inner 
    {           // (9)
    private:
        static int count;       // (10)
    };

void print() const {        // (11)
}
};

在(1)處的泛型模板Outer中,(4)處的code和(5)處print(),這兩個普通成員都具有一個外圍類模板。因此,需要使用一個template<>字首說明:後面將用一個模板實參集來對它進行全域性特化:

template<>
int Outer<void>::code = 12;

template<>
void Outer<void>::print() const
{
    std::cout << "Outer<void>";
}

這些定義將會用於替代類Outer<void>在(4)處和(5)處的泛型定義;但是,類Outer<void>的其它成員仍然預設地產生自(1)處的模板。另外,在提供了上面的宣告之後,就不能再次提供Outer<void>的顯式特化。

類似於全域性函式模板特化,我們需要一種可以在不指定定義的前提下(為了避免多處定義),可以宣告類模板普通成員特化的方法。儘管對於普通類的成員函式和靜態成員變數而言,非定義的類外宣告在C++中是不允許的;但如果是針對類模板的特化成員,該宣告則是合法的。也就是說,前面的定義可以具有如下宣告:

template<>
int Outer<void>::code;
template<>
void Outer<void>::print() const;

對於成員模板Outer<T>::Inner,也可以用一個特定的模板實參對它進行特化,而且對於該特化所在的外圍 Outer<T>而言,這個特化操作並不會影響 Outer<T>相應例項化體的其它成員。另外,由於存在一個外圍模板(也就是Outer<T>),所以我們需要新增一個template<>字首。最後所獲得的程式碼大致如下:

template<> template<typename X>
class Outer<wchar_t>::Inner 
{
public:
    static long count; // 成員型別發生了改變
};

template<> template<typename X>
long Outer<wchar_t>::Inner<X>::count;

模板Outer<T>::Inner也可以被全域性特化,但只能針對Outer<T>的某個給定例項。而且,我們需要新增兩個 template<>字首:因為外圍類需要一個 template<>字首,我們所要全域性特化的內圍模板(inner template)也需要一個template<>字首:

template<>
template<>
class Outer<char>::Inner<wchar_t> 
{
public:
    enum { count = 1};
};

// 下面的C++程式是不合法的:
// template<> 不能位於模板實參列表的後面
template<typename X>
template<> class Outer<X>::Inner<void>; // 錯誤

我們可以將上面這個特化與Outer<bool>的成員模板的特化比較一下。由於Outer<bool>已經在前面全域性特化了,所有它的成員模板也就不存在外圍模板,因此我們就只需要一個template<>字首:

template<>
class Outer<bool>::Inner<wchar_t> 
{
public:
    enum { count = 2 };
};

12.4 區域性的類模板特化

全域性模板特化通常都是很有用的, 但有時候我們更希望把類模板特化成一個“針對模板實參”的類家族, 而不是針對“一個具體實參列表”的全域性特化。 例如, 假設下面是一個實現連結串列功能的類模板:

template<typename T>
class List { // (1)
public:
	… 
	void append(T const&);
	inline size_t length() const;
	…
};

對於某個使用這個模板的大專案, 它可能會基於多種型別來例項化該模板的成員。 於是, 對於那些沒有進行內聯擴充套件的成員函式(譬如List<T>::append()) , 這就可能會明顯增加目的碼的大小。 然而, 如果我們從一個更低層次的實現來看, List<int*>::append()的程式碼和List<void*>::append()的程式碼是完全相同的。 也就是說, 我們希望可以讓所有的指標List共享同一個實現。 儘管我們不能直接用C++來表達這種實現, 但我們可以指定所有的指標List都例項化自一個不同的模板定義, 從而近似地獲得這種實現:

template<typename T>
class List < T* > 
{ // (2)
private:
	List<void*> impl;
	…
public:
	… 
	void append(T* p) 
	{
		impl.append(p);
	} 
	size_t length() const 
	{
		return impl.length();
	} …
};

在這種情況下, 我們把原來的模板(即(1) 處的模板) 稱為基本模板, 而後一個定義則被稱為區域性特化(因為該模板定義所使用的模板實參只是被區域性指定) 。 表示一個區域性特化的語法包括: 一個模板引數列表宣告(template<…>) 和在類模板名稱後面顯式指定的模板實參列表(在我們的例子中是<T*>) 。

我們前面的程式碼還存在一個問題, 因為 List<void*>會遞迴地包含一個相同型別的List<void*>成員。 為了打破這種無限遞迴, 我們可以在這個區域性特化前面先提供一個全域性特化:

template<>
class List<void*> 
{ // (3)
    … 
    void append (void* p);
    inline size_t length() const;
    …
};

這樣, 一切才是正確的。 因為當進行匹配的時候, 全域性特化會優於區域性特化。 於是, 指標List的所有成員函式都被委託給List<void*>的實現(通過容易內聯的函式) 。 針對C++模板備受指責的程式碼膨脹的缺點, 這也是克服該缺點的有效方法之一。

對於區域性特化宣告的引數列表和實參列表, 存在一些約束。 下面就是一些重要的約束:

1.區域性特化的實參必須和基本模板的相應引數在種類上(可以是型別、 非型別或者模板) 是匹配的。

2.區域性特化的引數列表不能具有預設實參; 但區域性特化仍然可以使用基本類模板的預設實參。

3.區域性特化的非型別實參只能是非型別值, 或者是普通的非型別模板引數; 而不能是更復雜的依賴型表示式(諸如2*N, 其中N是模板引數) 。

4.區域性特化的模板實參列表不能和基本模板的引數列表完全等同(不考慮重新命名) 。

下面的例子詳細地說明了這些約束:

template<typename T, int I = 3>
class S; // 基本模板

template<typename T>
class S < int, T > ; // 錯誤: 引數型別不匹配

template<typename T = int>
class S < T, 10 > ; // 錯誤: 不能具有預設實參

template<int I>
class S < int, I * 2 > ; // 錯誤: 不能有非型別的表示式

template<typename U, int K>
class S < U, K > ; // 錯誤: 區域性特化和基本模板之間沒有本質的區別

每個區域性特化(和每個全域性特化一樣) 都會和基本模板發生關聯。當使用一個模板的時候, 編譯器肯定會對基本模板進行查詢, 但接下來會匹配呼叫實參和相關特化的實參, 然後確定應該選擇哪一個模板實現。 如果能夠找到多個匹配的特化, 那麼將會選擇“最特殊”的特化(和過載函式模板所定義的原則一樣) ; 如果有未能找到“最特殊”的一個特化, 即存在幾個特殊程度一樣的特化, 那麼程式將會包含一個二義性錯誤。

最後, 我們應該指出: 類模板區域性特化的引數個數是可以和基本模板不一樣的; 既可以比基本模板多, 也可以比基本模板少。 讓我們再次考慮泛型模板List(在(1) 處宣告) 。 我們已經討論了應該如何優化指標List的實現, 但我們希望可以針對(特定的) 成員指標型別實現這種優化。 下面的程式碼就是針對指向成員指標的指標(pointer-to-memberpointers) , 來實現這種優化:

template<typename C>
class List < void* C::* >
{ // (4)
public:
	// 針對指向void*的成員指標的特化
	// 除了void*型別之外, 每個指向成員指標的指標型別都會使用這個特化
	typedef void* C::*ElementType;
	… 
	void append(ElementType pm);
	inline size_t length() const;
	…
};

template<typename T, typename C>
class List < T* C::* > { // (5)
private:
	List<void* C::*> impl;
	…
public:
	// 針對任何指向成員指標的指標型別的區域性特化
	// 除了指向void*的成員指標型別, 它在前面已經處理了
	// 我們看到這個區域性特化具有兩個模板引數
	// 然而基本模板卻只有一個引數
	typedef T* C::*ElementType;
	… 
	void append(ElementType pm) 
	{
		impl.append((void* C::*)pm);
	} 
	inline size_t length() const 
	{
		return impl.length();
	} 
	…
};

除了模板引數數量不同之外, 我們看到在(4) 處定義的公共實現本身也是一個區域性特化(對於簡單的指標例子, 這裡應該是一個全域性特化) , 而所有其它的區域性特化((5) 處的宣告) 都是把實現委託給這個公共實現。 顯然, 在(4) 處的公共實現要比(5) 處的實現更加特殊化, 因此也就不會出現二義性問題。

相關推薦

[C++ Template]深入模板--過載

目錄 第12章 特化與過載 目前為止,我們已經知道了:C++模板如何使一個泛型定義擴充套件成一些相關的類家族或者函式家族。雖然這是一個功能很強大的機制,但該機制並非適合於所有的情況;在一些情況下,這種泛型操作就不是特定模板引數替

C++模板

type 舉例 its 進一步 數據類型 類型 orm special template C++模板說到C++模板特化與偏特化,就不得不簡要的先說說C++中的模板。我們都知道,強類型的程序設計迫使我們為邏輯結構相同而具體數據類型不同的對象編寫模式一致的代碼,而無法抽取其中的

[轉]C++中模板

解析 匹配規則 創意 復雜 href 靈活 類模板特化 總結 行存儲 轉載自:http://hi.baidu.com/klcdyx2008/blog/item/5adbf77b79f316f90bd1873c.html 1.引言C++中的模板分為類模板和函數模板,雖然它引進

c++模板模板的全

1. c++模板的一般使用方法  1. 類模板 #include<iostream> using namespace std; template <typename T> class MyClass { public: MyClass <T>

C++模板(函式模板,類模板例項區別聯絡

一:例項化什麼是例項化:一個通過使用具體值替換模板引數,從模板產生的普通類,函式的過程1.顯示例項化:通過指定的型別,表明要例項化的型別2.隱式例項化:通過編譯器自己推演,判斷出要例項化的型別 二 :特

C++模板的偏

str emp ble 定義 col effective ron 導出 列表 模板的聲明 類模板和函數模板的聲明方式是一樣的,在類定義/模板定義之前聲明模板參數列表。例如: // 類模板 template <class T1, class T2> c

C++ templates類模板的幾點補充(Traits類模板)

前一篇文章《淺談C++ templates 函式模板、類模板以及非型別模板引數》簡單的介紹了什麼是函式模板(這個最簡單),類模板以及非型別模板引數。本文對類模板再做幾點補充。 文章目錄1. 預設的模板實參2. Traits程式設計技法——以STL迭代器為例1. 預設的模板實參這裡依舊使用上一篇文章中的arr

C++模板

【理論待補充...】 下面是一個函式模板特化的例子; /// 模版特化 template <class T> int compare(const T left, const T right) { std::cout << "in template<class

函式模板

模板為什麼要特化,因為編譯器認為,對於特定的型別,如果你能對某一功能更好的實現,那麼就該聽你的。 模板分為類模板與函式模板,特化分為全特化與偏特化。全特化就是限定死模板實現的具體型別,偏特化就是如果這個模板有多個型別,那麼只限定其中的一部分。 先看類模板:templa

C++使用模板實現工廠模式

許多C++程式設計師使用簡單工廠建立自己的物件,這時就會有很多這樣的分支,比如: class Staff { virtual double salary() = 0;//薪酬 } class Engineer : public Staff;//舉個栗子,不寫實現了 cla

6.4-資料結構&演算法-模板/函式模板/類模板/

一、為什麼要有模板? 將型別引數化,可以實現演算法與型別的分離,編寫針對型別更加抽象的函式或者類。   二、函式模板 通用定義: template<typename 型別形參1, ...> 返回型別 函式模板名 (形參表) { ...

template之全和偏

前言 關於講過traits萃取器的時候探討到偏特化的概念, 而在那一篇文章也沒有具體解釋偏特化是什麼, 怎麼實現, 所以可能在第一次看得時候會很莫名其妙. 所以我將偏特化放在其後講解, 為不明白的朋友做一個淺析的講解. 這裡我先聊一下全特化再聊偏特化. 全特化 全特化的模板引

template模板中classtypename區別

前言 在分析traits程式設計之前, 我們需要對模板引數型別tempname和class有一定的瞭解, 要明白他們在哪些方面不同, 哪些方面相同, 這樣才能對體會到traits程式設計的核心. 如果你已經明白了兩者, 那麼你可以直接看下一篇了. 相同之處 一般對模板引數型別

【全特化與偏特化】 1、普通類模板 template<class T,class N> class Template{}; 2、全特化。 template<> class Template<int,char>{}; 3、偏特化。

C++當函式模板遇上函式過載

//#include "stdafx.h" #include <iostream> using namespace std; void func(int a, int b) { cout << "普通函式:" <<

C++】深入理解“內聯巨集”

行內函數 內聯程式碼,程式無需跳到另一個位置執行程式碼,再跳回來。因此,行內函數執行速度比常規函式稍快,但代價是需要佔用更多記憶體。 所以應該有選擇性的使用行內函數,如果函式執行程式碼的時間比處理函式呼叫的 時間長,則即使使用行內函數,節省也沒啥明顯改進,而如果程式碼執行時間很短,則行內函數

c++】深入剖析虛擬繼承各種繼承關係中派生類內成員記憶體分佈情況及虛基類表的內容

概要 本文講述在VS2012環境下,採用程式碼和圖結合的方法,分析C++程式碼中不同繼承方式的物件模型,以及從彙編角度分析虛擬繼承編譯器生成的虛基類表裡的內容,不涉及虛擬函式。 繼承分類: 1.單繼承 一個子類只有一個直接父類 // 單繼承 工人類 繼承 人類 cl

C#程式設計之JSON序列反序列

1、在C#管理NuGet程式包中新增Json.NET 2、C#將物件序列化成JSON字串 模型類1 /// <summary> /// JSON字串模型.是否出錯 ///

模板特例過載

圖  1多型提要 靜多型(Static Polymorphism)是多型性的一種,繫結發生在編譯期(compile-time)(稱為靜態繫結static-binding)。 圖  2靜多型提要 非引數化多型和引數化多型並不衝突,而且相輔相成,它們混合使用能夠帶來

C++中的複製初始直接初始

C++ Primer裡說過,在C++中,初始化不等於賦值,初始化是指建立變數並給變數賦初值,而賦值是指擦除變數的當前值並用新值替換。C++中有兩種初始化方法,直接初始化和複製初始化。直接初始化是指使用 Object obj(patamer)這樣的語句,Object可以是內建