2009-02-28 7 views
12

あなたがテンプレートについて知っ最も重要なことは何を学びましたテンプレートを使用してライブラリ/ APIのほとんどを実装し始めており、実際に見られる最も一般的なパターン、ヒントなどを収集したいと考えています。最も重要なこと...レッスンは

私は、テンプレートについて学んだ最も重要なことは何ですか?

例を提供してみてください - 複雑かつ過度に乾燥した説明とは対照的に、理解することが容易になるだろう

おかげ

+0

これはコミュニティのwikiする必要があります。主観的です。 – strager

+0

私はそれが主観的だとは思わない。私が読んだことから、Sashaはテンプレートの使い方に関するヒントを求めています。しかし、あなたの編集が元の質問の意味を完全に変えてしまった可能性があります。その場合は、このコメントを無視してください。 – pyon

答えて

17

"例外C++スタイル" から、項目7:関数オーバーロードの解決は前にテンプレートの特殊化を発生します。オーバーロードされた関数とテンプレート関数の特殊化を混在させないでください。そうしないと、実際に関数が呼び出されるという驚くべきことに驚かされます。 項7の上に

template<class T> void f(T t) { ... } // (a) 
template<class T> void f(T *t) { ... } // (b) 
template<> void f<int*>(int *t) { ... } // (c) 
... 
int *pi; f(pi); // (b) is called, not (c)! 

さらに悪いことに、あなたがテンプレートの特殊タイプを省略した場合、まだ、異なる関数テンプレートは、定義順に、結果として応じて専門しまうかもしれません特殊な関数が呼び出されても呼び出されなくてもよい。

ケース1:

template<class T> void f(T t) { ... } // (a) 
template<class T> void f(T *t) { ... } // (b) 
template<> void f(int *t) { ... }  // (c) - specializes (b) 
... 
int *pi; f(pi); // (c) is called 

は、ケース2:

template<class T> void f(T t) { ... } // (a) 
template<> void f(int *t) { ... }  // (c) - specializes (a) 
template<class T> void f(T *t) { ... } // (b) 
... 
int *pi; f(pi); // (b) is called 
+0

から収集のヒントは、あなたがその一例を与えてもらえますか? –

+0

はい。私はこの人にも蹴られました。 –

+0

素敵な例! +1。 :) –

0

を別のコンパイルと実行ファイルサイズが大きいほど結果としての可能性を理解することが重要です。複数のC++ファイルで同じタイプのテンプレートをインスタンス化すると、少なくとも一部のコンパイラではそのタイプを何度も再現することになります。

+0

これらのコンパイラはどちらですか? –

+0

ええと、テンプレートインスタンシエーションは1つの定義ルールに違反しないので、私は確かに1つの翻訳単位以上のテンプレートをインスタンス化しました。しかし、コンパイラのテンプレートのサポートが貧弱であれば、WWIIIでも可能です。 –

+0

最新のリンカーは重複を破棄します。これは何年も続いています。それは実際にはコンパイラの問題ではありません。 –

4

この質問は、「関数を使用してライブラリのほとんどを実装しようとしていますが、関数を使用する際のよくある間違いは何ですか?そのような質問に賢明な答えを思いつくのは難しいですが、ここに私のアドバイスがあります - 良い本を読んでください。私は "C++ Templates" Vandevoordeによって& Josuttis、

+0

この本の一部を読んだことがあります。しかし、私はちょうどプログラムで遭遇する共通のヒントを収集したい。ありがとう... –

+2

@Neil:関数を使用する際のよくある間違いには、値を渡すか、ポインタ/参照でパラメータを渡すなどがあります。ポインタ/ローカル変数や一時変数への参照を返す。引数の評価の順序に依存しています(C++では保証しません)。 –

2

STLあなたの友人です。ここで

+0

必ずしもそうではありませんが、私はまだdownvoteがなぜわかりません。 – pyon

2

はいくつかのルールです:あなたは(STLとブーストの2つの顕著な例です)非常に、非常に汎用的なライブラリを書いている場合を除き

  1. は、任意のテンプレートを記述しないでください。
  2. 重要ではないテンプレートを何回もインスタンス化しないでください。巨大なテンプレートクラスをインスタンス化することは、特に不幸です。継承と多型を使用することを考慮する必要があります(単純なやり方は、仮想関数を使用することです)。
  3. constmutablevolatileをいつ使うか知っていれば、テンプレートのユーザーはコンパイルと実行の両方の時間を節約できます。
  4. テンプレートをインスタンス化する場合は、良いコンパイラを使用してください。
5

私はCoplienのCuriously Recurring Template Pattern (CRTP)は、私は自分自身が何度も何度も&に手を伸ばす見つける1つのテンプレートのトリックであると言っていると思います。本質的に、派生クラス名でパラメータ化された基本クラスから継承することにより、静的にカスタマイズされた機能を派生クラスに挿入することができます。おかしいと思いますが、驚くほど便利です(静的多型とも呼ばれます)。

また、「C++ Templates」を読み、AlexandrescuのModern C++ Designをスローするニールバターワースのアドバイスを2番目にします。

2

MeyersのEffective STL and C++の本と、AlexandrescuのModern C++ Designを読んでください。

マイヤーズは簡単な間違いやその回避方法の基本を教えてくれます。 Alexandrescuはテンプレートベースのプログラミングモデルを紹介しています。「は本当にがよいアイデアですか?本全体

+0

私はこれらの本の両方を読んで...人 –

2

私は、コードの重複を避けるために、コンパイルのチェックを通じて安全性を高めるために、テンプレートにかなり多くを使用する傾向があります。

一般に、コンパイラーが何をしようとしているのか、それぞれのタイプごとにコードがどのように生成されるのかを入力する際に​​考えるのが役立ちます。

非常に反復的な開発とテンプレートの複雑さの構築は、コンパイルエラーメッセージのシンクを避けるのに役立ちました。テンプレートの簡単な(またはモック)インスタンシエーションをどこかに保存することを忘れないでください。そうしないと、初めてモンスターテンプレートをインスタンス化するときに厄介な驚きが生じることがあります。

最後に、脱出できないときは、これらのコンパイルエラーメッセージを読んでください!彼らは最初はかなり怖かったかもしれませんが、本当に役に立ちます。最初に最初のものを抽出し、それをテキストエディタでコピーして見栄えのよいものにすると、それらに慣れるのに役立ち、すぐに読む第二の性質になります。

+1

SOURCEコードの繰り返しを避けるためには、どういう意味ですか?テンプレートを使用すると、MACHINEコードがかなり繰り返されるためです。 – pyon

7

これは普及していないかもしれませんが、私はそれが言われる必要があると思います。

テンプレートが複雑です。

これらはすごく強力ですが、賢明に使います。あまりにも夢中になってはいけません、あまりにも多くのテンプレート引数を持たないでください...あまりにも多くのスペシャライゼーションをしないでください...他のプログラマーもこれを読む必要があります。

そして、すべてのほとんどは、離れテンプレートメタプログラミングから滞在...日の

+0

しかし、テンプレートメタプログラミングは、そのすべての楽しい部分であるはずでした! – pyon

+0

"そして最も重要なことは、テンプレートメタプログラミングから離れてください..." 得点:-) – kyku

3

テンプレートヒント: は、あなたがテンプレートのインスタンスの選択した機能を特化することができます知っていました:

#include <iostream> 
#include <vector> 

namespace std { 
    template<> 
    void vector<int>::clear() { 
    std::cout << "Clearing..." << std::endl; 
    resize(0); 
    } 
} 

int main() { 
    std::vector<int> v; 
    v.push_back(1); 
    v.clear(); 
} 

ouputs: をクリア...

+0

私はそれを知っていました...良いこと –

4

一般的な間違いの1つは、テンプレートコンストラクタまたは代入演算子が、コンパイラが生成したものを抑制しないことです。

template <typename T> 
class A { 
public: 
    template <typename S> 
    A(A<S> const &); 

    template <typename S> 
    A & operator=(A<S> const &); 

private: 
    int * i; 
}; 

これらの関数はコピーコンストラクタやコピー代入演算子のように見えますが、コンパイラはそのようには見ず、暗黙のバージョンを生成します。結果は、オブジェクトが同じタイプからコピーされたかに割り当てられているときに、これらの機能によって実行されるアクション(。例えばメンバーのディープコピー)が行われないということです。

void foo (A<int>); 

void bar() { 
    A<int> a1; 
    foo (a1); // Implicitly generated copy ctor called 

    A<long> a2; 
    foo (a2); // Template ctor called. 

    A<int> a3; 
    a3 = a1; // Implicitly generated copy assignment operator called 

    a3 = a2; // Template assignment operator called 
} 

この挙動の理由が原因でありますオーバーロードの解決に特別なルール(13.3.3)へ:

これらの定義を考えると、実行可能な 関数F1は、別の実行可能な機能 F2であれば、すべての引数iについて、ICSI(より良い 関数であると定義されていますF1)は よりも悪い変換シーケンスではない I CSiを(F2)、次いで

[...]

- そのF1は、非テンプレート関数 であり、F2は、関数テンプレート 特殊であるか、または、そうでない場合、

上記の例で

は、オーバーロード解決は、テンプレートである一方は同じシグネチャを持つ2つの機能を、見ています。非テンプレート関数(暗黙的に生成されたコピーコンストラクタ/コピー代入演算子)が勝つので呼び出されます。

+0

それを見る別の方法は、コピーctorとoperator =はテンプレート内のテンプレートです。 A がインスタンス化されると、copy ctorおよびoperator = memberテンプレートはまだ実際の関数として存在しないため、A に対して暗黙のcopy ctorおよびoperator =が生成されます。 –

+1

代入演算子でこれを避けるには、テンプレートメンバーに代入する非テンプレートメンバーを使用できます。A&operator =(const A&a){return operator = (a); }別の非テンプレートメンバーを書くことを除いて、コピーctorに対してこれを行う方法はありません。 –

0

私はC++を使用して、より高度なテンプレートメタプログラミングを含め、多くのことをテンプレートと私の気持ちは、その有用性が過大評価されていることですしました。もともとは、C++を作成した後も、一般的なプログラミング機能に取り組むために、C++言語に追加されていました。これにより、型を意識せずにコードのロジックに焦点を当てるだけで、コードを明確にし再利用できるようになります。

私のプログラミングの哲学は、言語の本来の目的と設計を理解することです、それは本当に言語を理解するために機能です。私はテンプレートのメタプログラミングがテンプレートの束縛であると感じているので、避けるべきです。しかし、テンプレートは、タプルの場合のようなより高いレベルのジェネリック型を定義するのに便利です。

関連する問題