C++ - Module 07

18908 ワード

かたわく


関数やクラスを書き直す必要がなく、複数のデータ型で使用できるフレームワーク.
c++では、テンプレートは2つに分けることができます.
関数テンプレート、クラステンプレート.

関数テンプレート


関数を作成する場合、関数の機能は明確ですが、そのタイプは明確ではありません.
既存の上書きメソッドを使用して様々なデータ型を処理するには、
int sum(int a, int b){
   return a + b;
}
double sum(double a, double b){
   return a + b;
}
上記の方法で各資料タイプに関数を定義します.しかし,この方法は時間も関数の内容も同じであるが,資料型のせいで変化し,これはあまりにも非効率である.この問題を解決する機能はテンプレートです.
template <typename T>
T sum(T a){
   return a;
}
or(1つのテンプレートですが、列挙変数は2つでも多くでも構いません.)
template <typename T>
T sum(T a, T b){
    return a + b;
}
このようにテンプレートを用いると,各資料型をそれぞれ宣言する必要がなく,すべての資料型を得ることができる.

かに変数資料の種類が違うとき。


もし、かに変数資料型の種類が2つ以上ある場合、資料型が違うとしたら?
sum(1, 1.0); // 앞에껀 int형이고 뒤에껀 double이다. 
上記のようにコードを回します.
test.cpp:12:15: error: no matching function for call to 'sum'
template <typename T>
T sum(T a, T b){
    return a + b;
}
明らかに1つの関数がT sum(T a,T b)の2つの列挙変数を受け入れ,不整合なエラーが発生した.資料型は2種類ありますが、templateにも2種類もらえます.
template <typename T1, typename T2>
T1 printAll(T1 a, T2 b){
	return a + (T1)b;
}
このようにMegger変数のtypename T 2を追加すると、エラーは発生しません.
では、3つの時は?

列挙変数タイプ2個、列挙変数3個

  3 template <typename T, typename T2>
  4 T sum(T a, T2 b, T c){
  5     std::cout << sizeof(T2) << std::endl;
  6     return a + b + c;
  7 }
int test1 = 1;
double test2 = 2;
int test3 = 1;
  
sum(test1, test2, test3); // 앞에껀 int형이고 뒤에껀 double이다. 
コードを見てください.Megger変数は3つありますがintとdoubleタイプのみなので,sum(test 1,test 2,test 3)と定義します.配合が良好で、運行が良好である.

template<typenameT,typenameT 2>罰を受ける順番


もう一つテストしましょう.
 3 template <typename T, typename T2>
 4 T sum(T a, T b, T2 c){
 5     std::cout << sizeof(T2) << std::endl;
 6     return a + b + c;
 7 }
11     int test1 = 1;
12     int test2 = 1;
13     double test3 = 1;
14
15     std::cout << sum(test1, test2, test3) << std::endl;
16
もしtest 2がdoubleではなくtest 3 doubleだったら?
これも同じで、T 2は双形になりました.typename個数が列挙変数個数より小さい場合は、タイプ順に受信し、typename個数が同じ場合は順次受信する.
正確な順序がわからないただ,受け取ったtypenameがsumにどのように入れられるかを推測すると,templateが受け取る順序も異なる.

テンプレートを特殊化!


関数テンプレートを使用して、任意のデータ型の入力を関数として処理します.ただし,特定の資料型が入ると,単独で処理したい場合もある.この場合,関数テンプレートの特殊化は非常に有用である.
sum(1, 1); // 앞에껀 int형이고 뒤에껀 double이다. 
template <typename T>
T sum(T a, T b){
    return a + b;
}
  
template <> //템플릿 특수화
T sum(int a, int b){
  return a + b;
  }
これでint型の資料型が入ってくると.
T sum(int a, int b)
で動作し、int以外のデータ型を入力します.
T sum(T a, T b)
処で処理する.

クラステンプレート。


クラステンプレート(class template)もクラスの一般化である.クラステンプレートを定義すると、タイプに応じてクラスを作成できます.
関数テンプレートとは異なり、関数はテンプレート引数を明示的に作成する必要はありませんが、クラステンプレートはテンプレート引数を指定する必要があります.
test<int> tmp; //<int>이런식으로 템플릿에 들어갈 자료형을 명시해주어야함.
なぜなら、クラスのオブジェクトを作成するからです.インスタンス化すると、オブジェクトにメモリが割り当てられ、ジェネレータが呼び出されます.ただし、クラステンプレートのデータ型を決定するには、ジェネレータを呼び出す必要があります.したがって、テンプレート引数が明示的に作成されていない場合は、メモリをどのタイプに割り当てるべきか分からないため、オブジェクトを作成できないため、オブジェクトの作成時にテンプレート引数を指定する必要があります.

クラステンプレートの使用方法

  //main.cpp file
  template <typename T1>
  class aa
  {
      // 클래스 멤버의 선언
    public :
      T1 test;
  };
  
  int main(void)
  {
  	aa<int> aa_1;
  	return 0;
  }
template <typename T1>
クラスでは上記のように宣言し、クラスではT 1を資料型として使用すればよい.
代わりに使うときは、どんな資料型を使っても、対象を作るときに入れればいい.

クラステンプレートメンバー関数の定義

//main.cpp file
#include <iostream>

template <typename T1>
class aa
{
  // 클래스 멤버의 선언
public :
  T1 test;
	void tp_print(T1 data);
};

template <typename T1>
void aa<T1>::tp_print(T1 data)
{
	std::cout << data << std::endl;
}

int main(void)
{
	aa<int> aa_1;
	aa_1.tp_print(2);
	return 0;
}
tp printが定義されています.tp printで使用されるT 1リポジトリのテンプレートを再定義し、aaの範囲指定子を使用するときに、どのリポジトリのaaであるかを指定します.
aa<T1>::
このように使います.
### 클래스 템플릿 특수화
//aa.hpp
#ifndef __AA_HPP__
#define __AA_HPP__

template <typename T1>
class aa
{
  // 클래스 멤버의 선언
public :
  T1 test;
	void tp_print(T1 data);
};

template <>
class aa<int>
{
  // 클래스 멤버의 선언
public :
  int test;
	void tp_print(int data);
	int example(char data2);
};

#endif
関数テンプレートの特殊化に似ています.
テンプレートに入力されたデータ型がintの場合にのみ、特別な操作が行われます.
残りのデータ型は、既存の定義されたテンプレートクラスに入ります.

クラステンプレートの特定部分

  template <typename T1, typename T2>
class aa{...};
  
  
  template <> class aa<int, double> {......};

  template <template T1> class aa<T1, double> {......};
クラステンプレートをより細かく配布できます.

クラステンプレートをネストします。

template <typename T>
class X
{
  template <typename U>
  class Y
  {
      ...
  }
  ...
  }

int main(void)
{
  ...
}

template <typename T>
template <typename U>
X<T>::Y<U>::멤버함수이름()
{
  ...
}
クラステンプレートにクラステンプレートが含まれている場合.

クラステンプレートの継承

template <typename T1>
class aa
{
  // 클래스 멤버의 선언
public :
  T1 test;
	void tp_print(T1 data);
};

template <typename U>
class bb : public aa<U> {
	U money;
public:
	Derived(U a, U b) : aa<U>(a) { money = b; }
}
aaという名前のクラステンプレートが作成されました.
両親aaの子供bb.bbでは、親aaのテンプレートタイプをUに入れると、既存aaを使用する場合と同様に使用できます.
サブオブジェクトをテンプレート形式で書きたくない場合は、サブオブジェクトを定義する場合は、一般化ではなく明確に定義するだけです.
class bb : public aa<int> {
	int asset;
public:
	Derived(int a, int b) : aa<int>(a) { asset = b; }
	void showDerived() { cout << asset << endl; }
};
ご覧のように、両親aaはtypenameを受け入れず、int型に決定します.これによりmainでサブタイプを使用する場合、テンプレートリポジトリタイプを定義する必要がなく、親のテンプレートリポジトリでintタイプを使用できます.

分類テンプレートファイルの取り外し


極めて重要である
先ほどの例maincpp hoylee.cpp hoylee.hppに分けましょう

ファイル配布エラーの例


クラステンプレートを通常のクラスファイルに分割し、コンパイル中にエラーが発生しました.
//main.cpp
#include <iostream>
#include "aa.hpp"
int main(void)
{
	aa<int> aa_1;
	aa_1.tp_print(2);
	return 0;
}
//aa.hpp
#ifndef __AA_HPP__
#define __AA_HPP__

template <typename T1>
class aa
{
  // 클래스 멤버의 선언
public :
  T1 test;
	void tp_print(T1 data);
};

#endif
//aa.cpp
#include "aa.hpp"

template <typename T1>
void aa<T1>::tp_print(T1 data)
{
	std::cout << data << std::endl;
}
clang++ main.cpp aa.cpp
誤って、次のように、プライマリ文の作成時にリンクできません.

templateを削除して普通のクラスに変更してコンパイルすると、正常にコンパイルされていることを確認できます.
これがテンプレートクラスのみがコンパイルされる理由です.
コンパイルプロセスをさらに理解する必要があります.
コンパイル時clang++main.cpp aa.cppを行っても,2つのファイルはコンパイル時に互いに参照し合い,コンパイルしない.
すなわちmainをバイナリ実行可能ファイルにするにはclass,mainを定義する必要がある.cppの内容だけではクラスがどのように定義されているかは判断できません.
これらの問題を解決するには2つの方法がある.

分類テンプレートファイルの解体ソリューション1

//main.cpp
#include <iostream>
#include "aa.hpp"

int main(void)
{
	aa<int> aa_1;
	aa_1.tp_print(2);
	return 0;
}
//aa.hpp
#ifndef __AA_HPP__
#define __AA_HPP__

template <typename T1>
class aa
{
  // 클래스 멤버의 선언
public :
  T1 test;
	void tp_print(T1 data);
};

#include "aa.tpp"
#endif
//aa.tpp
#include <iostream>

	template <typename T1>
void aa<T1>::tp_print(T1 data)
{
	std::cout << data << std::endl;
}
aa.tppは誤字ではありません.tpp拡張子とは限らない.
大事なのは.aa.hppの一番下
#include "aa.tpp"
aaと宣言するtppファイルにtp ptrintが定義されています.
目の先の人は知っているはずだ、aa.hppファイルでtp ptrintファイルを定義するのと同じです.
分離じゃなくaahppファイルで定義しました.
ファイルを分離する理由は、コードの管理と拡張が容易になるためであることを忘れないでください.

クラステンプレートファイル分割ソリューション2

//main.cpp
#include <iostream>
#include "aa.hpp"
#include "aa.cpp"

int main(void)
{
	aa<int> aa_1;
	aa_1.tp_print(2);
	return 0;
}
#ifndef __AA_HPP__
#define __AA_HPP__

template <typename T1>
class aa
{
  // 클래스 멤버의 선언
public :
  T1 test;
	void tp_print(T1 data);
};

#endif
//aa.cpp
#include <iostream>
#include "aa.hpp"

	template <typename T1>
void aa<T1>::tp_print(T1 data)
{
	std::cout << data << std::endl;
}
main.cppを見ると.
#include "aa.cpp"
aa.cppファイルを読み込みました.mainでコンパイルするときにaaを使用します.mainをコンパイルするとき、cppファイルに関する情報が見つからないため、コンパイルエラーが発生します.コンパイルのためにcppが含まれます.
注意:
https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file
https://koey.tistory.com/113
https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=vjhh0712v&logNo=221561418596