【c++templates読書ノート】【4】テクニック的基礎知識
6415 ワード
1、キーワードtypename
キーワードtypenameを導入したのは、テンプレート内部の識別子がタイプであってもよいことを説明するためである.テンプレートパラメータに依存する名前がタイプの場合はtypenameを使用します.
2、thisを使う
ベースクラスで宣言され、テンプレートパラメータに依存するシンボル(関数や変数など)については、this->またはBase::::を使用します.
3、メンバーテンプレート
ネストされたクラスとメンバー関数は、テンプレートとして使用できます.例:
4、テンプレートのテンプレートパラメータ
テンプレートパラメータ自体がテンプレートである場合、このパラメータはテンプレートのテンプレートパラメータです.関数テンプレートでは、テンプレートのテンプレートパラメータはサポートされていません.
例:
5、文字列を関数テンプレートの実パラメータとして使用する
上記の例では、VS 2013でエラーが実行されている.
上記の例では、後の2つの呼び出しエラーの問題は、長さの違いにより、これらの文字列は異なる配列タイプに属し、「apple」と「peach」は同じタイプのchar const[6]、「tomato」のタイプはchar const[7]であるため、最初の呼び出しのみが合法であるということである.ただし、非参照パラメータを宣言し、異なる文字列をパラメータとして使用できます.
上記の2番目の呼び出しは、非参照タイプのパラメータに対して、実演中にポインタへの配列変換が発生するため、正しいです.しかし、ここでは2つの文字列の辞書順ではなくポインタを比較します.
キーワードtypenameを導入したのは、テンプレート内部の識別子がタイプであってもよいことを説明するためである.テンプレートパラメータに依存する名前がタイプの場合はtypenameを使用します.
template<T>
class MyClass{
typename T::SubType * ptr; // typename SubType T , typename,SubType T
...
};
2、thisを使う
ベースクラスで宣言され、テンプレートパラメータに依存するシンボル(関数や変数など)については、this->またはBase
#include<iostream>
using namespace std;
template<typename T>
class Base{
public:
void exit(){ cout << "exit" << endl; }
};
template<typename T>
class Derived:Base<T>{
public:
void exitDerived(){
this->exit();// VS 2013 exit()
}
};
int main(){
Derived<int> d;
d.exitDerived();
system("pause");
return 0;
}
3、メンバーテンプレート
ネストされたクラスとメンバー関数は、テンプレートとして使用できます.例:
#include<iostream>
#include<deque>
using namespace std;
template<typename T>
class Stack{
private:
deque<T> deq;
public:
void push(T const& elem);
void pop();
T top() const;
bool empty() const{
return deq.empty();
}
template<typename T2>//
Stack<T>& operator=(Stack<T2> const& op2);
};
template<typename T>
void Stack<T>::push(T const& elem){
deq.push_back(elem);
}
template<typename T>
void Stack<T>::pop(){
if (deq.empty()){
throw out_of_range("Stack<>::pop():empty stack");
}
deq.pop_back();
}
template<typename T>
T Stack<T>::top() const{
if (deq.empty()){
throw out_of_range("Stack<>::pop():empty stack");
}
return deq.back();
}
template<typename T>
template<typename T2>
Stack<T>& Stack<T>::operator=(Stack<T2> const& op2){
if ((void*)this == (void*)&op2)
return *this;
Stack<T2> tmp(op2);
deq.clear();
while (!tmp.empty()){
deq.push_front(tmp.top());
tmp.pop();
}
return *this;
}
int main(){
try{
Stack<float> istk;
istk.push(7.2);
istk.push(8.3);
istk.push(9.4);
cout << istk.top() << endl;
istk.pop();
cout << istk.top() << endl;
Stack<int> sstk;
sstk = istk;
cout << sstk.top() << endl;
sstk.pop();
cout << sstk.top() << endl;
}
catch (exception const& ex){
cerr << "Exception:" << ex.what() << endl;
return EXIT_FAILURE;
}
system("pause");
return 0;
}
4、テンプレートのテンプレートパラメータ
テンプレートパラメータ自体がテンプレートである場合、このパラメータはテンプレートのテンプレートパラメータです.関数テンプレートでは、テンプレートのテンプレートパラメータはサポートされていません.
例:
#include<iostream>
#include<deque>
#include<vector>
using namespace std;
// Container
template<typename T,template<typename ELEM,typename=allocator<ELEM>> class Container=deque>
class Stack{
private:
Container<T> container;
public:
void push(T const& elem);
void pop();
T top() const;
bool empty() const{
return container.empty();
}
template<typename T2, template<typename ELEM2, typename = allocator<ELEM2>> class Container2>
Stack<T, Container>& operator=(Stack<T2, Container2> const& op2);
};
template<typename T, template<typename ELEM, typename = allocator<ELEM>> class Container>
void Stack<T,Container>::push(T const& elem){
container.push_back(elem);
}
template<typename T, template<typename ELEM, typename = allocator<ELEM>> class Container>
void Stack<T, Container>::pop(){
if (container.empty()){
throw out_of_range("Stack<>::pop():empty stack");
}
container.pop_back();
}
template<typename T, template<typename ELEM, typename = allocator<ELEM>> class Container>
T Stack<T, Container>::top() const{
if (container.empty()){
throw out_of_range("Stack<>::pop():empty stack");
}
return container.back();
}
template<typename T, template<typename ELEM, typename = allocator<ELEM>> class Container>
template<typename T2, template<typename ELEM2, typename = allocator<ELEM2>> class Container2>
Stack<T, Container>& Stack<T, Container>::operator=(Stack<T2, Container2> const& op2){
if ((void*)this == (void*)&op2)
return *this;
Stack<T2, Container2> tmp(op2);
container.clear();
while (!tmp.empty()){
container.push_front(tmp.top());
tmp.pop();
}
return *this;
}
int main(){
try{
Stack<double> dstk;
dstk.push(7.2);
dstk.push(8.3);
dstk.push(9.4);
cout << dstk.top() << endl;
dstk.pop();
cout << dstk.top() << endl;
Stack<int> istk;
istk = dstk;
cout << istk.top() << endl;
istk.pop();
cout << istk.top() << endl;
}
catch (exception const& ex){
cerr << "Exception:" << ex.what() << endl;
return EXIT_FAILURE;
}
system("pause");
return 0;
}
5、文字列を関数テンプレートの実パラメータとして使用する
#include<iostream>
#include<string>
using namespace std;
template<typename T>
T const& Max(T const& a, T const& b){
return a < b ? b : a;
}
int main(){
string s = "abc";
cout << Max("apple", "peach") << endl;
//cout << Max("apple", "tomato") << endl;// ,
//cout << Max("apple", s) << endl;// ,
system("pause");
return 0;
}
上記の例では、VS 2013でエラーが実行されている.
上記の例では、後の2つの呼び出しエラーの問題は、長さの違いにより、これらの文字列は異なる配列タイプに属し、「apple」と「peach」は同じタイプのchar const[6]、「tomato」のタイプはchar const[7]であるため、最初の呼び出しのみが合法であるということである.ただし、非参照パラメータを宣言し、異なる文字列をパラメータとして使用できます.
#include<iostream>
#include<string>
using namespace std;
template <typename T>
T Max(T a, T b){
return a < b ? b : a;
}
int main(){
string s = "abc";
cout << Max("apple", "peach") << endl;
cout << Max("apple", "tomato") << endl;// ,
//cout << Max("apple", s) << endl;// ,
system("pause");
return 0;
}
上記の2番目の呼び出しは、非参照タイプのパラメータに対して、実演中にポインタへの配列変換が発生するため、正しいです.しかし、ここでは2つの文字列の辞書順ではなくポインタを比較します.