Effective C++ (7): Templates and Generic Programming
8797 ワード
Introduction
この章は主にテンプレートのプログラミングを紹介し、最も核心的な思想はできるだけ運行期間のしたことをコンパイル期間の完成まで移動することである.最後に以下のTMPについても簡単に紹介した.
Rule 41: Understand implicit interfaces and compile-time polymorphism
暗黙的なプログラミングとコンパイル期間の多様性を理解する.1つのRuleについて理解する必要があるのはtemplateのマルチステートがclassesのマルチステートと異なることであり、1つは発生とコンパイル期間であり、1つは運行期間である.効率的にはtemplateの方が速い.しかしtemplateがインタフェースの一致を保つには暗黙的(implicit)であり、すなわちtemplate関数を書く場合、w.size()のような関数を用いると、実際には暗黙的にこのtypeにsizeというインタフェース関数が必要となる.コードを書くのも難しくなりました
Remeber: classesおよびtemplatesは、インタフェースおよびマルチステート をサポートします. classesにとってインタフェースは明示的であり、関数署名を中心とする.マルチステートはvirtual関数によって実行期間 で発生する. templateパラメータでは、インタフェースは暗黙的(implicit)であり、有効な式に基づいて、マルチステートはtemplateの具体化と関数の再ロード解析によってコンパイル期間 で発生する.
Rule 42: Understand the two meanings of typename
コードを見ればいいです.
Remeber: templateパラメータを宣言する場合、class=typename キーワードtypenameを使用してネストされた依存タイプ名を表しますが、base class lists(ベースクラス列)またはmember initialization list(メンバー初期値列)内でbase class修飾子 として使用することはできません.
Rule 43: Know how to access names in templatized base classes
これはコンパイラがその中のCompanyにsendClearという関数があるかどうかを判断できないためである(特化の問題を考慮).したがって、base class templateの任意の特化バージョンが一般的な(汎化)バージョンで提供されるインタフェースをサポートすることをコンパイラに承諾するには、「this->」を追加する必要があります.
「this->」の他にも2つの方法があります. using宣言式、例えばusing MsgSender::sendClear は、MsgSender::sendClear(info)のような明確な資格修飾子を用いる.「virtualバインド動作」 を閉じるため、この方法は推奨されません.
Remeber:template classを継承する場合は、「this->」でbase class templates内のメンバー名を呼び出すか、「base class資格修飾子」と書くことで完了します.
Rule 44: Factor parameter-independent code out of templates
templateの使用はコードの膨張の問題を引き起こす可能性がある.特に、非タイプパラメータ(non-type parameter)については、次のように注意してください.
上のsize_t nは非タイプのパラメータであり、nが5,10をとると、定数5と10が異なる以外は2つのコードが生成する、それによってコードが膨張しやすい.この問題を解決する最善の方法は、操作されたコードを1つのbase template classに配置し、データの問題がある場合はbase template classにデータを指すポインタを保存し、本当のデータをderived template classに配置し、derived classは非タイプパラメータを受け入れ、derived template classにはデータを保存する以外に、他の関数はbaseの関数を直接呼び出すので、重複するコードにはderived classのコードしかありません.例:
このようなnon-type template parametersに加えて、type template parametersも膨張を引き起こす.例えばポインタを含む場合は
Remeber: Templatesはパラメータに基づいて複数のコードを生成し、コード冗長性 をもたらしやすい.非タイプテンプレートパラメータ(non-type template parameters)によるコード膨張は、templateパラメータを関数パラメータまたはclassメンバー変数で置き換えることができる. タイプパラメータ(type parameters)によるコード膨張は、完全に同じバイナリ記述(binary representation)を有する表現タイプ共有実装コード を実現することができる.
Rule 45: Use member function templates to accept “all compatible types”
メンバー関数テンプレートを使用してすべての互換性タイプを受信
例を挙げると、次のようになります.
これがテンプレートクラスで定義汎化テンプレート関数である.
Remeber: member function templates(メンバー関数テンプレート)を使用して、「すべての互換性タイプを受信可能」関数 を生成してください.「汎用copy構造」または「汎用assignment操作」に使用するmember templatesを宣言した場合は、通常のcopy構造関数とcopy assignmentオペレータ を宣言する必要があります.
Rule 46: Define non-member functions inside templates when type conversions are desired
前述のように、「すべてのパラメータのタイプ変換」の関数については、non-member functionとして、暗黙的なタイプ変換を提供する.しかしtemplate classには違いがある.関数をfriend関数として定義し、クラス内で実装する必要があります.
Remeber:class templateを作成するとき、彼が提供した関数が「すべてのパラメータの暗黙的なタイプ変換」をサポートしている場合は、それらの関数を「class template内部のfriend関数」と定義してください.
Rule 47: Use traits classes for information and types
まず、iteratorの5つの異なるタイプを導入します. input_iterator:一方向で、毎回 しか操作できません. output_iterator:同上 forward_iterator:前の2つの反復器のいずれかを行うことができ、 を複数回操作することができます. bidirectional_iterator:双方向操作可能 random_access_iteratror:最も強力で、定数時間で勝手にジャンプ はい、次にTraints classes+関数のリロードを紹介して、次の問題を解決します.iteratorをジャンプさせる関数を書く必要があります.異なるiteratorタイプによって異なる実装があります.
では、2つの問題を解決します.1.IterTがどのタイプのiteratorかをどう判断するか.2.コンパイラでタイプによって実装が異なる場合
この場合、私たちはTraits classesを採用してiteratorにタイプ情報を挟む.簡単に言えばiteratorを定義することですtraitsは、タイプ情報を伝達し、各iterator自身でtag情報を定義し、iterator_traitsはこのtagの情報を伝えることです
Remeber: Traits classesは、「タイプ関連情報」をコンパイル期間中に利用可能にする.彼らはtemplatesと「templates特化」で を実現した.リロード・テクノロジー(overloading)を統合すると、traints classesはコンパイル中にタイプにif...elseテスト を実行する可能性があります.
Rule 48: Be aware of template metaprogramming
テンプレートメタプログラミング(TMP)は図霊完備であることが証明され、以前Rule 47はTMPの一例であったが、TMPは面倒で直感的ではないため、ここではTMPのHello worldを簡単に紹介し、factorialを求める.
Remeber:Template metaprogramming(TMP、テンプレートメタプログラミング)は、実行期間からコンパイル期間に作業を移すことができ、早期のエラー検出とより高い実行効率を実現することができます.
シリーズ記事
Effective C++ (1): Accustoming Yourself to C++ Effective C++ (2): Constructors, Destructors, and Assignment Operators Effective C++ (3): Resource Management Effective C++ (4): Designs and Declaration Effective C++ (5): Implementation Effective C++ (6): Inheritance and Oject-Oritent Design Effective C++ (7): Templates and Generic Programming Effective C++ (8): Customizing new and delete Effective C++ (9): Miscellany
この章は主にテンプレートのプログラミングを紹介し、最も核心的な思想はできるだけ運行期間のしたことをコンパイル期間の完成まで移動することである.最後に以下のTMPについても簡単に紹介した.
Rule 41: Understand implicit interfaces and compile-time polymorphism
暗黙的なプログラミングとコンパイル期間の多様性を理解する.1つのRuleについて理解する必要があるのはtemplateのマルチステートがclassesのマルチステートと異なることであり、1つは発生とコンパイル期間であり、1つは運行期間である.効率的にはtemplateの方が速い.しかしtemplateがインタフェースの一致を保つには暗黙的(implicit)であり、すなわちtemplate関数を書く場合、w.size()のような関数を用いると、実際には暗黙的にこのtypeにsizeというインタフェース関数が必要となる.コードを書くのも難しくなりました
Remeber:
Rule 42: Understand the two meanings of typename
コードを見ればいいです.
template //template class
void print2nd(const C& container)
{
typename C::const_iterator iter(container.begin()); // , typename
}
Remeber:
Rule 43: Know how to access names in templatized base classes
base class templates
を継承する場合は、base class templatesのメンバーを「this->this」で呼び出す必要があります.例:template
class MsgSender{
public:
...
void sendClear(const MsgInfo& info)
{
std::string msg;
Company c;
c.sendCleartext(msg);
}
void sendSecret(const MsgInfo& info)
{ ... }
};
template
class LoggingMsgSender: public MsgSender
{
public:
void sendClearMsg(const MsgInfo &info)
{
...
sendClear(info); //
this->sendClear(info); //
...
}
};
これはコンパイラがその中のCompanyにsendClearという関数があるかどうかを判断できないためである(特化の問題を考慮).したがって、base class templateの任意の特化バージョンが一般的な(汎化)バージョンで提供されるインタフェースをサポートすることをコンパイラに承諾するには、「this->」を追加する必要があります.
「this->」の他にも2つの方法があります.
Remeber:template classを継承する場合は、「this->」でbase class templates内のメンバー名を呼び出すか、「base class資格修飾子」と書くことで完了します.
Rule 44: Factor parameter-independent code out of templates
templateの使用はコードの膨張の問題を引き起こす可能性がある.特に、非タイプパラメータ(non-type parameter)については、次のように注意してください.
template
class SquareMatrix{
public:
...
void invert();
};
上のsize_t nは非タイプのパラメータであり、nが5,10をとると、定数5と10が異なる以外は2つのコードが生成する、それによってコードが膨張しやすい.この問題を解決する最善の方法は、操作されたコードを1つのbase template classに配置し、データの問題がある場合はbase template classにデータを指すポインタを保存し、本当のデータをderived template classに配置し、derived classは非タイプパラメータを受け入れ、derived template classにはデータを保存する以外に、他の関数はbaseの関数を直接呼び出すので、重複するコードにはderived classのコードしかありません.例:
template //
class SquareMatrixBase{
protected:
SquareMatrixBase(std::size_t n, T* pMem):
size(n), pData(pMem) {}
void setDataPtr(T* ptr) { pData = ptr; }
void invert(std::size_t matrixSize);
...
private:
std::size_t size;
T* pData; //
};
template
class SquareMatrix: private SquareMatrixBase{
public:
SquareMatrix() : SquareMatrixBase(n, data) {}
...
private:
T data[n*n];
};
このようなnon-type template parametersに加えて、type template parametersも膨張を引き起こす.例えばポインタを含む場合は
list
とlist
であるが、代わりにvoid*
を用いることが望ましい.Remeber:
Rule 45: Use member function templates to accept “all compatible types”
メンバー関数テンプレートを使用してすべての互換性タイプを受信
例を挙げると、次のようになります.
template
class shared_ptr{
public:
template
explicit shared_ptr(Y* p); //
template
shared_ptr(shared_ptr const& r); //
...
};
これがテンプレートクラスで定義汎化テンプレート関数である.
Remeber:
Rule 46: Define non-member functions inside templates when type conversions are desired
前述のように、「すべてのパラメータのタイプ変換」の関数については、non-member functionとして、暗黙的なタイプ変換を提供する.しかしtemplate classには違いがある.関数をfriend関数として定義し、クラス内で実装する必要があります.
template
class Ration{
public:
...
friend const Rational operator*(const Rational& lhs, const Rational& rhs) // friend
{
return Rational(lhs.numerator() * rhs.numerator(),
lhs.denominator() * rhs.denominator());
} //
};
Remeber:class templateを作成するとき、彼が提供した関数が「すべてのパラメータの暗黙的なタイプ変換」をサポートしている場合は、それらの関数を「class template内部のfriend関数」と定義してください.
Rule 47: Use traits classes for information and types
まず、iteratorの5つの異なるタイプを導入します.
template
void advance(IterT &iter, DistT d);
では、2つの問題を解決します.1.IterTがどのタイプのiteratorかをどう判断するか.2.コンパイラでタイプによって実装が異なる場合
この場合、私たちはTraits classesを採用してiteratorにタイプ情報を挟む.簡単に言えばiteratorを定義することですtraitsは、タイプ情報を伝達し、各iterator自身でtag情報を定義し、iterator_traitsはこのtagの情報を伝えることです
template
struct iterator_traits{ // IterT iterator_category "IterT ", iterator_traits
typedef typename IterT::iterator_category iterator_category; // typedef typename ,
...
};
/* */
template
struct iterator_traits{
typedef random_access_iterator_tag iterator_category;
...
};
template <...>
class deque{
public:
class iterator{
public:
typedef random_access_iterator_tag iterator_category; // , random_access_iterator_tag struct
...
};
...
};
/* doAdvance traits , advance traits */
template
void advance(IterT &iter, DistT d)
{
doAdvance(iter, d,
typename std::iterator_traits::iterator_category());
}
template
void doAdvance(IterT &iter, DistT d,
std::random_access_iterator_tag)
{
iter += d;
}
template
void doAdvance(IterT &iter, DistT d,
std::bidirectional_iterator_tag)
{
if ( d >= 0) { while (d--) ++iter; }
else { while(d++) --iter; }
}
Remeber:
Rule 48: Be aware of template metaprogramming
テンプレートメタプログラミング(TMP)は図霊完備であることが証明され、以前Rule 47はTMPの一例であったが、TMPは面倒で直感的ではないため、ここではTMPのHello worldを簡単に紹介し、factorialを求める.
template
struct Factorial{
enum { value = n * Factorial::value };
};
template<> //
struct Factorial<0>{
enum { value = 1 };
};
int main(){
std::cout<<< Factorial<10>::value ;
}
Remeber:Template metaprogramming(TMP、テンプレートメタプログラミング)は、実行期間からコンパイル期間に作業を移すことができ、早期のエラー検出とより高い実行効率を実現することができます.
シリーズ記事
Effective C++ (1): Accustoming Yourself to C++ Effective C++ (2): Constructors, Destructors, and Assignment Operators Effective C++ (3): Resource Management Effective C++ (4): Designs and Declaration Effective C++ (5): Implementation Effective C++ (6): Inheritance and Oject-Oritent Design Effective C++ (7): Templates and Generic Programming Effective C++ (8): Customizing new and delete Effective C++ (9): Miscellany