什么叫偏特化压?

DaNiao 2003-01-24 03:45:28
rt
...全文
119 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
_炳叔_ 2003-01-25
  • 打赏
  • 举报
回复
up
ckacka 2003-01-25
  • 打赏
  • 举报
回复
是支持的吧?
DaNiao 2003-01-25
  • 打赏
  • 举报
回复
原来就是这个压,叫个这么奇怪的名字

我感觉VC好象是支持这个的压

比如<string>
里就应该有很多basic_string<char>的

难道VC的STL不是这么做的吗?
ckacka 2003-01-25
  • 打赏
  • 举报
回复
首先讓我們檢驗 "std::pair" 的定義,為了簡化,我略去其中的 comparision operators, default constructor, 和 template copy constructor:

template <typename T1, typename T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;

T1 first;
T2 second;

pair(const T1 & nfirst, const T2 & nsecond)
:first(nfirst), second(nsecond) { }
};
此刻這個 "pair" 無法持有 references,因為如此一來其 constructor 將被迫接受一個 reference to reference,而這種語法目前並不存在 [參考資料7]。讓我們想想,為了允許 "pair" 得以持有 non-reference 型別、references 型別、constant references 型別,constructor 的參數必須是什麼樣子:

"T1" 的型別 constructor 的參數型別
T
const T &

T &
T &

const T &
const T &


一個和 type traits classes 類似的技術,允許我們建構單一的對應關係,使我們得以根據 contained class 的型別決定參數型別。type traits classes 提供了一個 "add_reference" 轉換,可以為自身型別加上一個 reference,除非它本身已經是一個 reference。

"T1" 的型別 "const T1" 的型別 "add_reference<const T1>::type" 的型別
T
const T
const T &

T &
T & [註8]
T &

const T &
const T &
const T &


這使我們得以建立一個 template 主版本,定義一個可內含 non-reference 型別、 reference 型別、constant reference 型別的 "pair" :

template <typename T1, typename T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;

T1 first;
T2 second;

pair(boost::add_reference<const T1>::type nfirst,
boost::add_reference<const T2>::type nsecond)
:first(nfirst), second(nsecond) { }
};
為它回添標準的 comparision operators, default constructor 和 template copy constructor 之後(它們都和原先版本相同),你就有了一個可以內含 reference 型別的 std::pair。

當然我們也可以使用偏特化技巧完成同樣的擴充,但果真如此,我們需要三個 "pair" 偏特化版本和一個主版本。Type traits 允許我們僅僅定義一個主版本,就可以自動而神奇地將自己調整為任何偏特化版,取代一一偏特化的所謂「暴力法」。以此方式使用 type traits,可允許程式員將偏特化授權(delegate)給 type traits classes,使得代碼比較容易維護,也比較容易被理解。

結論
希望這篇文章能夠給你一些想法,讓你大略知道 type-traits 是什麼。boost 說明文件中有更完整的 classes 列表,以及更進一步的使用範例。Templates 使 C++ 有能力實現泛型編程所帶來的復用性;這篇文章還告訴你,templates 可以和 generic 一樣地美好。這都有賴 type traits 帶來的價值。

致謝
感謝 Beman Dawes 和 Howard Hinnant 對本文所提的意見。

參考資料
Nathan C. Myers, C++ Report, June 1995.
這個 type traits library 的完成,要感謝 Steve Cleary, Beman Dawes, Howard Hinnant 和 John Maddock。你可以在 www.boost.org 找到它。
所謂純量型別(scalar type)就是算術型別(例如內建的整數或浮點數)、列舉型別(enumeration)、指標、函式指標、或以上任何型別再加上 const- 或 volatile- 修飾詞。
此句引自 Donald Knuth, ACM Computing Surveys, December 1974, pg 268.
這一份測試代碼是 boost utility library 的一部份(見 algo_opt_examples.cpp),以 gcc 2.95 編譯完成,所有最佳化選項都打開。我的測試結果是在 400MHz Pentium II + Microsoft Windows 98 上獲得。
John Maddock 和 Howard Hinnant 已經送出一個 "compressed_pair" library 給 Boost,其中使用的一個技術,和此處所描述的技術類似,也是用來持有 references。他們的 pair 也使用 type traits 來決定是否有任何型別是空的,並且採用 "derive" 而非 "contain" 的方式,用以保存空間 -- 這正是 "compressed" 的命名由來。
這其實是 C++ 核心語言工作小組的一個議題,由 Bjarne Stroustrup 提出。暫時的解決辦法是,允許 "a reference to a reference to T" 的意義等同於 "a reference to T",但是只能存在於 template 具現實體中,或是存在於一個「具備多個 const-volatile 修飾詞」的 method 中。
為什麼這裡不該有 const 修飾詞呢?對此感到驚訝的人,我要提醒你,請記住, references 永遠是個隱晦常數(舉個例子,你不能夠重新對一個 reference 賦值)。同時也請你記住,"const T &" 是完全不同的東西。因為這些理由,template 型別引數如果本身是個 references 的話,其「const-volatile 修飾詞」都被忽略。
 

列表一
namespace detail{

template <bool b>
struct copier
{
template<typename I1, typename I2>
static I2 do_copy(I1 first,
I1 last, I2 out);
};

template <bool b>
template<typename I1, typename I2>
I2 copier<b>::do_copy(I1 first,
I1 last,
I2 out)
{
while(first != last)
{
*out = *first;
++out;
++first;
}
return out;
}

template <>
struct copier<true>
{
template<typename I1, typename I2>
static I2* do_copy(I1* first, I1* last, I2* out)
{
memcpy(out, first, (last-first)*sizeof(I2));
return out+(last-first); // 譯註:因為是 RandomAccessIterator
}
};

}

template<typename I1, typename I2>
inline I2 copy(I1 first, I1 last, I2 out)
{
typedef typename
boost::remove_cv<
typename std::iterator_traits<I1>
::value_type>::type v1_t;

typedef typename
boost::remove_cv<
typename std::iterator_traits<I2>
::value_type>::type v2_t;

enum{ can_opt =
boost::is_same<v1_t, v2_t>::value
&& boost::is_pointer<I1>::value
&& boost::is_pointer<I2>::value
&& boost::has_trivial_assign<v1_t>::value
};

return detail::copier<can_opt>::do_copy(first, last, out);
}

ckacka 2003-01-25
  • 打赏
  • 举报
回复
转一片文章,大家看看

C++ Type traits

by John Maddock and Steve Cleary
DDJ 2000/10

 

譯者:陳崴
侯捷註:本文係北京《程序員》雜誌 2001/06 的文章。譯筆順暢,技術飽滿。
承譯者陳崴先生與《程序員》雜誌負責人蔣濤先生答允,
轉載於此,以饗臺灣讀者,非常感謝。

未得陳崴先生與蔣濤先生二人之同意,任何人請勿將此文再做轉載。


--------------------------------------------------------------------------------

泛型編程編出來的代碼,適用於任何「吻合某種條件限制」的資料型別。這已成為撰寫可復用代碼時的一個重要選擇。然而,總有一些時候,泛型不夠好 — 有時候是因為不同的型別差距過大,難以產生一致的泛化實作版本。這個時候 traits 技術就變得相當重要。這種技術可以將那些需要被納入考量的型別性質以一種 type by type 的原則,封裝於一個 traits class 內,於是可以將「由於型別之間的差異,必須撰寫出來以備用」的代碼體積降至最低,並使泛用代碼的體積提昇到最高。

考慮一個例子:當我們處理字元字串(character strings)時,常見的一個操作行為就是決定「以 null 為結束符號」的字串的長度。很明顯我們不可能寫出一份泛型代碼取代眾所周知原本就存在的極有效率的解法:是的,C 標準函式 strlen 和 wcslen 通常是以組合語言完成,如果再加上適量的硬體支援,就能夠比 C++ 泛型版本有明顯的速度優勢。C++ 標準程式庫的作者了解這一點,所以他們抽取出 char 和 wchar_t 的性質,置於 class char_traits 內。於是,泛型代碼一旦處理字元字串,便可以簡單地使用 char_traits<>::length 來決定一個「以 null 為結束符號」的字串的長度,並且很安心地確知 char_traits 的特化版本將採用最適當的方法來完成任務。

Type traits(型別特性)
Class char_traits 是「把一系列與型別相關的性質包裹於單一 class 之內」的典型例子,那正是 Nathan Myers 所謂的 baggage class [參考資料1]。在 Boost type-traits library 中,我們 [參考資料2] 完成了一組非常特別的 traits classes,其中每一個 classes 都封裝了 C++ 型別系統中的一個(僅僅一個)特性。所謂特性(trait)指的是,舉個例子,某型別是否為一個 pointer,或是一個 reference?某型別是否擁有一個 trivial constructor,或是擁有一個 const 修飾詞? 這些 type-traits classes 共同享有一致性的設計:每一個 class 都有一個 member value,那是一個編譯期常數,如果某型別擁有某種特性,此一常數的值就是 true,否則就是 false。稍後我將為你展示,這些 classes 可以被使用於泛型編程之中,用來決定某個型別的特性,並導入對應的最佳化措施。

Boost type-traits library 也內含一組 classes,可以針對某個型別執行專屬的特定的轉換。例如它們可以從某個型別身上移除一個上層的 const 或 volatile。每一個用來執行轉換的 class 都定義有一個 typedef-member type,那便是轉換結果。所有這些 type-traits classes 都被定義於 namespace boost 之中。為求簡化,本文的範例代碼大多省略命名空間的設定。

實作(Implementation)
要在這裡顯示 type-traits library 的所有實作內容,是不可能的,那真是太多太多了。如果你有這個需求,請看 Boost library 的源碼。大部份實作方法都是重複的,所以這裡我只給你一種風貌,為你示範這些 classes 如何實作出來。讓我們從程式庫中最簡單的一個 class 開始。is_void<T> 有一個 member value,如果 T 是 void,它就是 true。

template <typename T>
struct is_void
{ static const bool value = false; };

template <>
struct is_void<void>
{ static const bool value = true; };
在這裡,我們定義了 template class is_void 的一個主版本,並針對「T 是 void」的情況提供了一個全特化( full-specialisation)版。雖然 template class 的全特化是一項重要技術,但有時候我們需要的解決方案介於「完全泛化」和「完全特化」之間。這正是標準委員會之所以定義出偏特化(partial template-class specialisation)的原因。舉個例子,讓我們考慮 class boost::is_pointer<T>,這裡我們需要一個主版本,用來處理「T 不為指標」的所有情況,以及一個偏特化版本,用來處理「T 是指標」的情況:

template <typename T>
struct is_pointer
{ static const bool value = false; };

template <typename T>
struct is_pointer<T*>
{ static const bool value = true; };
偏特化的語法帶了點不可思議的味道,而且一談到它很容易就耗掉一整篇文章。就像全特化的情形一樣,為了針對某個 class 寫出一個偏特化版本,你首先必須宣告 template 主版本。偏特化版本在 class 名稱之後多出一個 <…> ,其中內含偏特化參數;這些參數定義出「將被繫結於偏特化版」的某些型別。究竟什麼參數會(或說能夠)出現於偏特化版本之中,規則頗為曲折,以下是一個簡略的規則。如果你能夠以此型式合法寫出兩個多載化函式:

void foo(T);
void foo(U);
那麼你就能夠以此型式寫出一個偏特化版本:

template <typename T>
class c{ /*details*/ };

template <typename T>
class c<U>{ /*details*/ };
這個簡則並非絕對成立,但它非常簡單,足以讓你牢牢記住並足夠接近精確的規則。

至於比較複雜的偏特化例子,讓我們考慮 class remove_bounds<T>。這個 class 定義了唯一一個 typedef-member type,其型別與 T 相同,但移除任何上層(top level)的 array 邊界;這是「traits class 對某個型別進行轉換」的例子:

template <typename T>
struct remove_bounds
{ typedef T type; };

template <typename T, std::size_t N>
struct remove_bounds<T[N]>
{ typedef T type; };
remove_bounds 的目的是:想像一個泛型演算法,接受一個 array 型別做為 template 參數,remove_bounds 會提供一個方法,讓你有辦法得知底部(underlying)的 array 型別。例如,remove_bounds<int[4][5]>::type 會被核定為型別 int[5]。這個例子也向你展示,在一個偏特化版本中,template 參數的個數並不需要吻合 default template 中的個數。然而,出現於 class 名稱之後的參數個數必須吻合 default template 的參數個數和參數型別。

copy 最佳化
現在我要舉一個例子,說明我們可以如何運用 type traits classes。考慮標準程式庫所提供的 copy 演算法:

template<typename Iter1, typename Iter2>
Iter2 copy(Iter1 first, Iter1 last, Iter2 out);
很明顯,寫一個泛型版本的 copy 絕無問題,它可以處理任何型別的迭代器 Iter1和 Iter2。然而在某種情況下,copy 動作可以透過 memcpy 完成。為了能夠以 memcpy 完成 copy,以下條件必須成立:

兩個迭代器 Iter1 和 Iter2 的型別都必須是指標。
Iter1 和 Iter2 都必須指向相同的型別 - 但允許有不同的 const 和volatile 修飾詞。
Iter1 所指的型別必須有一個 trivial assignment operator。
所謂 trivial assignment operator,我的意思是這個型別如果不是一個純量型別(scalar types)[參考資料3],就是:

這個型別沒有使用者自定的 assignment operator。
這個型別沒有任何 data members 採用 reference 型式。
所有的 base classes,以及所有的 data member objects 都有 trivial assignment operators。
如果上述所有條件都獲得滿足,那麼這個型別就可以被 memcpy 直接拷貝,而不使用一個由編譯器產生的 assignment operator。type-traits library 提供了一個 class has_trivial_assign,使得當 T 有一個 trivial assignment operator 時,has_trivial_assign<T>::value 為 true。這個 class 只能對純量型別起作用,但你很輕易就可以將它特殊化,使它適用於那些也擁有 trivial assignment operator 的 class/struct。換一個角度說,如果 has_trivial_assign 給出錯誤的答案,它會導致安全性方面的錯誤。

列表一顯示一個最佳化(使用 memcpy)的 copy 代碼。代碼之中首先定義一個 template class copier,接受唯一一個 template 參數 Boolean,然後是一個 static template member function do_copy,執行 copy 的泛型版本(也就是比較慢但比較安全的版本)。接下來是一個 copier<true> 特化版本,其中也定義了一個 static template member function do_copy,這一次使用 memcpy 來執行最佳化拷貝動作。

為了完成整份實作代碼,現在我們需要一個 copy 版本;如果可以安全使用 memcpy,就讓它呼叫 copier<true>::do_copy 執行特化版本,否則就呼叫 copier<false>::do_copy 執行泛化版本。這正是列表一的代碼的所作所為。為了了解這些代碼如何運作,請看 copy 函式代碼,並首先注意最前面的兩個 typedefs v1_t 和 v2_t。它們使用 std::iterator_traits<Iter1>::value_type 來得知兩個迭代器所指的是什麼型別,然後將其結果餵給另一個 type-traits class remove_cv,用以移除上層的 const- 或 volatile-修飾詞,這使 copy 得以比較兩個型別而不在乎 const- 或 volatile- 修飾詞。接下來,copy 宣告一個列舉元 can_opt,它將成為 copier 的 template 參數 - 在這裡,宣告為常數只是為了方便:數值可以被直接傳遞給 class copier(譯註:我無法理解這一段意思;代碼本身並未出現常數宣告)。can_opt 的值是根據「以下所有項目都驗證為真」而計算出來:

首先,兩個迭代器必須指向相同型別 - 驗證方法是透過 type-traits class is_same。
其次,兩個迭代器都必須是真正的指標 - 驗證方法是透過先前描述過的 class is_pointer。
最後,被迭代器所指的型別必須有一個 trivial assignment operator - 驗證方法是透過 has_trivial_assign。
最後,我們可以使用 can_opt 的值做為 template 引數,傳給 copier。這裡所呈現的 copy 版本會根據它所獲得的參數而調
chinajiji 2003-01-25
  • 打赏
  • 举报
回复
请看这个贴子,其中有我与其它人的回复:
http://expert.csdn.net/Expert/topic/1358/1358833.xml?temp=.9169886
熊主任 2003-01-24
  • 打赏
  • 举报
回复
搬个小板凳,回头慢慢看!
toybearcn 2003-01-24
  • 打赏
  • 举报
回复
Partial Specialization
A practical example
There’s nothing to prevent you from using a class template in any way you’d use an ordinary class. For example, you can easily inherit from a template, and you can create a new template that instantiates and inherits from an existing template. If the vector class does everything you want, but you’d also like it to sort itself, you can easily reuse the code and add value to it:
//: C06:Sorted.h
// Template specialization
#ifndef SORTED_H
#define SORTED_H
#include <string>
#include <vector>

template<class T>
class Sorted : public std::vector<T> {
public:
void sort();
};

template<class T>
void Sorted<T>::sort() { // A bubble sort
for(int i = size(); i > 0; i--)
for(int j = 1; j < i; j++)
if(at(j-1) > at(j)) {
// Swap the two elements:
T t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}

// Partial specialization for pointers:
template<class T>
class Sorted<T*> : public std::vector<T*> {
public:
void sort();
};

template<class T>
void Sorted<T*>::sort() {
for(int i = size(); i > 0; i--)
for(int j = 1; j < i; j++)
if(*at(j-1) > *at(j)) {
// Swap the two elements:
T* t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}

// Full specialization for char*:
template<>
void Sorted<char*>::sort() {
for(int i = size(); i > 0; i--)
for(int j = 1; j < i; j++)
if(std::strcmp(at(j-1), at(j)) > 0) {
// Swap the two elements:
char* t = at(j-1);
at(j-1) = at(j);
at(j) = t;
}
}
#endif // SORTED_H ///:~

The Sorted template imposes a restriction on all classes it is instantiated for: They must contain a > operator. In SString this is added explicitly, but in Integer the automatic type conversion operator int provides a path to the built-in > operator. When a template provides more functionality for you, the trade-off is usually that it puts more requirements on your class. Sometimes you’ll have to inherit the contained class to add the required functionality. Notice the value of using an overloaded operator here – the Integer class can rely on its underlying implementation to provide the functionality.Comment
The default Sorted template only works with objects (including objects of built-in types). However, it won’t sort pointers to objects so the partial specialization is necessary. Even then, the code generated by the partial specialization won’t sort an array of char*. To solve this, the full specialization compares the char* elements using strcmp( ) to produce the proper behavior.Comment
Here’s a test for Sorted.h that uses the unique random number generator introduced earlier in the chapter:Comment
//: C06:Sorted.cpp
// Testing template specialization
//{L} ../TestSuite/Test
//{-g++295}
//{-msc}
#include "Sorted.h"
#include "Urand.h"
#include "../arraySize.h"
#include <iostream>
using namespace std;

char* words[] = {
"is", "running", "big", "dog", "a",
};
char* words2[] = {
"this", "that", "theother",
};

int main() {
Sorted<int> is;
Urand<47> rand;
for(int i = 0; i < 15; i++)
is.push_back(rand());
for(int l = 0; l < is.size(); l++)
cout << is[l] << ' ';
cout << endl;
is.sort();
for(int l = 0; l < is.size(); l++)
cout << is[l] << ' ';
cout << endl;

// Uses the template partial specialization:
Sorted<string*> ss;
for(int i = 0; i < asz(words); i++)
ss.push_back(new string(words[i]));
for(int i = 0; i < ss.size(); i++)
cout << *ss[i] << ' ';
cout << endl;
ss.sort();
for(int i = 0; i < ss.size(); i++)
cout << *ss[i] << ' ';
cout << endl;

// Uses the full char* specialization:
Sorted<char*> scp;
for(int i = 0; i < asz(words2); i++)
scp.push_back(words2[i]);
for(int i = 0; i < scp.size(); i++)
cout << scp[i] << ' ';
cout << endl;
scp.sort();
for(int i = 0; i < scp.size(); i++)
cout << scp[i] << ' ';
cout << endl;
} ///:~

Each of the template instantiations uses a different version of the template. Sorted<int> uses the “ordinary,” non-specialized template. Sorted<string*> uses the partial specialization for pointers. Lastly, Sorted<char*> uses the full specialization for char*. Note that without this full specialization, you could be fooled into thinking that things were working correctly because the words array would still sort out to “a big dog is running” since the partial specialization would end up comparing the first character of each array. However, words2 would not sort out correctly, and for the desired behavior the full specialization is necessary.Comment
北极猩猩 2003-01-24
  • 打赏
  • 举报
回复
偏特例化(template partial specialization)
假设你有模板定义:
template<class T, class U>
class MyTemp{
....
}
可以使用
template<class T>
class MyTemp<T, const T*>{
....
}

当你使用MyTemp<char, const char*>时将调用第二个模板定义
toybearcn 2003-01-24
  • 打赏
  • 举报
回复
template 的 partial specialization?

qhgary 2003-01-24
  • 打赏
  • 举报
回复
是不是印错了::)
DaNiao 2003-01-24
  • 打赏
  • 举报
回复
我也不懂所以才问

前面有人说VC不支持偏特化云云
我听不大懂
h3wolf 2003-01-24
  • 打赏
  • 举报
回复
不懂,什么呀?

69,371

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧