天天看點

typename C++(第一次使用CSDN寫部落格)typename關鍵字1.概念2.使用方法3.使用域4.使用周期5.使用原因6.花絮7.近期用到typename的源代碼

typename關鍵字

@1.概念(what)

2.使用方法(how)

3.使用域(where)

4.使用生存期(when)

5.使用原因(why)

6.花絮

7. 近期用到typename的源代碼

1.概念

“ typename"是一個C++程式設計語言中的關鍵字。相當用于泛型程式設計時是另一術語"class"的同義詞 這個關鍵字用于指出模闆聲明(或定義)
  中的非獨立名稱(dependent names)是類型名,而非變量名。
           

eg:

templata<calss T>
calss Myclass{
  typename T::SubType *ptr;
  ...
};
           

2.使用方法

(1)typename關鍵字用于引入一個模闆參數。 
           
templata<typename T>
           
(2)typename關鍵字用于顯式地告訴編譯器”T::bar”是一個類型名 ,避免産生二義性
           
template <typename T>
void foo(const T& t)
{   
    // 聲明一個指向某個類型為T::bar的對象的指針
    typename T::bar * p;
}
           

3.使用域

這是一項C++程式設計語言的泛型程式設計(或模闆程式設計)的功能,typename關鍵字用于引入一個模闆參數。eg:
           
//定義一個傳回參數中較大的通用函數
templata<typename T>
const T& max(const T& x,const T& y){
  return x > y?x:y;
};
           
//可将上述代碼中的typename替換成class,此時class 與 typename 是等效的
templata<typename T>
const T& max(const T& x,const T& y){
  return x > y?x:y;
};
           

4.使用周期

typename關鍵字是系統庫自帶的。tynename是伴随系統庫存在的。
           

5.使用原因

考慮以下代碼
           
template<typename T>
void foo(const T& t){
//聲明一個指向某種類型為T::bar的對象的指針
    T::bar * p;
}

struct StructWithBarAsType{
    typedef int bar;
};

main(){
    StructWithBarAsType x;
    foo(x);
}
           
這段代碼看起來能通過編譯,但是,事實上這段代碼并不正确。因為編譯器并不知道T::bar是一個類型的名字還是某種變量的一個變量名,
即T::bar存在二義性。究其根本,造成這種歧義的原因在于,編譯器不明白T::bar到底是不是”模闆參數的得獨立名字“,簡稱”非獨立名字“。
注意,任何含有名為"bar"的項類T,都可以被當作模闆參數傳入foo()函數,包括typename類型、枚舉類型或者變量等。為了消除歧義,
C++語言标準規定:**A name used in a template declaration or definition and that is dependent on a template-parameter is
 assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the
 keyword typename.**
 意即出現上述歧義時,編譯器将自動預設bar為一個變量名,而不是類型名。是以上面例子中的代碼 T::bar * p 會被解釋為乘法,而不是
 聲明p為指向T::bar類型的對象的指針。


如果還有另一個名為StructWithBarAsValue類型,如下:
           
struct StructWithBarAsValue
{
    int bar;
};
           
那麼,編譯器将以完全不同的方式來解釋 T::bar * p 的含義。
           

解決問題的最終辦法,就是顯式地告訴編譯器,T::bar是一個類型名。這就必須用typename關鍵字,例如:

template <typename T>
void foo(const T& t)
{   
    // 聲明一個指向某個類型為T::bar的對象的指針
    typename T::bar * p;
}
           
這樣,編譯器就确定了T::bar是一個類型名,p也就自然地被解釋為指向T::bar類型的對象的指針了。
           

6.花絮

(1) typename,除了做為C++的一個關鍵字,還作為C++的函數。 這裡就不做介紹了。
 
 (2)一些關鍵概念
    a.限定名與非限定名
      a.1限定名(qualified name),故名思義,是限定了命名空間的名稱。看下面這段代碼,cout和endl就是限定名.
           注:cout和endl前面都有std::,它限定了std這個命名空間,是以稱其為限定名.
           
#include <iostream>
main()  {
    std::cout << "Hello world!" << std::endl;
}

           
a.2如果在上面這段代碼中,前面用using std::cout;或者using namespace std;,然後使用時隻用cout和endl,它們的前面不再
 有空間限定std::,是以此時的cout和endl就叫做非限定名(unqualified name)。

b.依賴名和非依賴名
b.1依賴名(dependent name)是指依賴于模闆參數的名稱
b.2非依賴名(non-dependent name)則 相反,指不依賴于模闆參數的名稱。看下面這段代碼:
           
template <class T>
class MyClass {
    int i;
    vector<int> vi;
    vector<int>::iterator vitr;
 
    T t;
    vector<T> vt;
    vector<T>::iterator viter;
}
           
因為是内置類型,是以類中前三個定義的類型在聲明這個模闆類時就已知。然而對于接下來的三行定義,隻有在模闆執行個體化時才能知道它們的
類型,因為它們都依賴于模闆參數T。是以,T, vector<T>和vector<T>::iterator稱為依賴名。前三個定義叫做非依賴名。

更為複雜一點,如果用了typedef T U; U u;,雖然T沒再出現,但是U仍然是依賴名。由此可見,不管是直接還是間接,隻要依賴于模闆參數,
該名稱就是依賴名。
 
 c.類作用域
 在類外部通路類中的名稱時,可以使用類作用域操作符,形如MyClass::name的調用通常存在三種:靜态資料成員、靜态成員函數和嵌套類型:
     注:MyClass::A, MyClass::B, MyClass::C分别對應着上面三種。
           
struct MyClass {
    static int A;
    static int B();
    typedef int C;
}
           
(3)關于typename關鍵字的曆史
 Stroustrup在最初起草模闆規範時,他曾考慮到為模闆的類型參數引入一個新的關鍵字,但是這樣做很可能會破壞已經寫好的很多程式
 (因為class已經使用了很長一段時間)。但是更重要的原因是,在當時看來,class已完全足夠勝任模闆的這一需求,是以,為了避免
 引起不必要的麻煩,他選擇了妥協,重用已有的class關鍵字。是以隻到ISO C++标準出來之前,想要指定模闆的類型參數隻有一種方
 法,那便是使用class。這也解釋了為什麼很多舊的編譯器隻支援class。

但是對很多人來說,總是不習慣class,因為從其本來存在的目的來說,是為了差別于語言的内置類型,用于聲明一個使用者自定義類型。
那麼對于下面這個模闆函數的定義(相對于上例,僅将typename換成了class):
           
//定義一個傳回參數中較大的通用函數
templata<class T>
const T& max(const T& x,const T& y){
return x > y?x:y;
};
           
從表面上看起來就好像這個模闆的參數應該隻支援使用者自定義類型,是以使用
 語言内置類型或者指針來調用該模闆函數時總會覺得有一絲奇怪(雖然并沒有
 錯誤):
           
int v1 = 1, v2 = 2;
int ret = compare(v1, v2);
 
int *pv1 = NULL, *pv2 = NULL;
ret = compare(pv1, pv2);
           
令人感到奇怪的原因是,class在類和模闆中表現的意義看起來存在一些不一緻,前者針對使用者自定義類型,而後者包含了語言内置類型和
   指針。也正因為如此,人們似乎覺得當時沒有引入一個新的關鍵字可能是一個錯誤。

   這是促使标準委員會引入新關鍵字的一個因素,但其實還有另外一個更加重要的原因,和文章最開始那行代碼相關。
   在Stroustrup起草了最初的模闆規範之後,人們更加無憂無慮的使用了class很長一段時間。可是,随着标準化C++工作的到來,人們發現
   了模闆這樣一種定義:
           
template <class T>
void foo() {
    T::iterator * iter;
    // ...
}
           
這段代碼的目的是什麼?多數人第一反應可能是:作者想定義一個指針iter,它指向的類型是包含在類作用域T中的iterator。可能存在這樣
   一個包含iterator類型的結構:
           
struct ContainsAType {
    struct iterator { /*...*/ };
    // ...
};
           
然後像這樣執行個體化foo:
           
foo<ContainsAType>();
           
這樣一來,iter那行代碼就很明顯了,它是一個ContainsAType::iterator類型的指針。
    到目前為止,咱們猜測的一點不錯,一切都看起來很美好。
    在類作用域一節中,我們介紹了三種名稱,由于MyClass已經是一個完整的定義,是以編譯期它的類型就可以确定下來,也就是說MyClass::
    A這些名稱對于編譯器來說也是已知的。

 可是,如果是像T::iterator這樣呢?T是模闆中的類型參數,它隻有等到模闆執行個體化時才會知道是哪種類型,更不用說内部的iterator。通過
 前面 類作用域一節的介紹,我們可以知道,T::iterator實際上可以是以下三種中的任何一種類型:

靜态資料成員
靜态成員函數
嵌套類型
前面例子中的ContainsAType::iterator是嵌套類型,完全沒有問題。可如果是靜态資料成員呢?如果執行個體化foo模闆函數的類型是像這樣的:
           
struct ContainsAnotherType {
    static int iterator;
    // ...
};
           
然後如此執行個體化foo的類型參數:
           
foo<ContainsAnotherType>();
           
那麼,T::iterator * iter;被編譯器執行個體化為ContainsAnotherType::iterator * iter;,這是什麼?前面是一個靜态成員變量而不是類型,
那麼這    便成了一個乘法表達式,隻不過iter在這裡沒有定義,編譯器會報錯:

 **error C2065: ‘iter’ : undeclared identifier**

對于用于模闆定義的依賴于模闆參數的名稱,隻有在執行個體化的參數中存在這個類型名,或者這個    名稱前使用了typename關鍵字來修飾,編譯器才會将該名稱當成是類型。除了以上這兩種情況,絕不會被當成是類型。

是以,如果你想直接告訴編譯器T::iterator是類型而不是變量,隻需用typename修飾:
           
template <class T>
void foo() {
    typename T::iterator * iter;
    // ...
}

           
這樣編譯器就可以确定T::iterator是一個類型,而不再需要等到執行個體化時期才能确定,是以消除了前面提到的歧義。
           

7.近期用到typename的源代碼

(1)[Template] Swap Values
Write a generic function that swap values in two variables. You should test the function with int, double and string.
Using the following function header:

template<typename T>
void swapVar(T &var1, T &var2)
           
//source.h
template<typename T>
void swapVar(T &var1, T &var2){
	T temp; 
	temp = var1;
	var1 = var2;
	var2 = temp;
}
           
//framework.cpp
#include <iostream>
#include <string>
#include "source.h"
using namespace std;

int main()
{
  //freopen("result.txt","w",stdout);
  int v1 = 1;
  int v2 = 2;
  swapVar(v1, v2);
  cout << v1 << " " << v2 << endl;

  double d1 = 1.1;
  double d2 = 2.5;
  swapVar(d1, d2);
  cout << d1 << " " << d2 << endl;

  string str1="aa";
  string str2="abc";
  swapVar(str1,str2);
  cout<< str1 <<" "<< str2 << endl;

  char c1='a';
  char c2='b';
  swapVar(c1,c2);
  cout<< c1 <<" "<< c2 << endl;


  return 0;
}

           
(2)[Template]Template Compare (eden)
Haoran is a "huge god" who loves C++ so much.One day he wants to implement a template function cmp which compares any type of two elements.Now he passes the mission to you all and hopes you can implement it and makes it suitable to compare any two int,** double, float, char, string and any two same type of pointers.*If the first parameter is equal to the second one, then return true, elsewise, false.

You can be careful that:

(1)When comparing two int, double, float, char*, string , you should compare their values.

(2)When comparing two pointers, you should compare the values they point to.

(3)the cmp function should always return a boolean value.
           
//cmp.h
#ifndef CMP_H
#define CMP_H
#include <string>
using std::string;
bool cmp(char *a,char *b){
    string A(a),B(b);
    return A==B;
}
template <typename T>
bool cmp(T a,T b){
    return a==b;
}
template <typename T>
bool cmp(T *a,T *b){
    return *a==*b;
}

#endif
           
//main.cpp
#include <iostream>
#include <string>
#include "cmp.h"
using std::cout;
using std::endl;
using std::string;
int main() {
     int aInt = 1, bInt = 2;
     double aDouble = 3.0, bDouble = 3.0;
     char aChars[5] = "haha", bChars[5] = "hahb";
     char taChars[6] = "trick", tbChars[6] = "trick";
     string aStr = "haha", bStr = "aha";
     int* aIntPtr = &aInt, *bIntPtr = &bInt;
      cout << cmp(aInt, bInt)<< endl;
     cout << cmp(aDouble, bDouble)<< endl;
      cout << cmp(aChars, bChars)<< endl;
     cout << cmp(taChars, tbChars)<< endl;
     cout << cmp(aStr, bStr)<< endl;
     cout << cmp(aIntPtr, bIntPtr)<< endl;
     cout << cmp(&aDouble, &bDouble) << endl;
     return 0;
}
  
           
(3) [Template] typename! typename!Given the following two classes TypeA, and TypeB: 
  class TypeA
  {
   public:
  class SubType
 {
  public:
   string toString() {return "subType in TypeA";};
  }; 
};

class TypeB
{
public:
 class SubType
{
public:
  string toString() {return "subType in TypeB";};
};
};

Write a template class MyClass that has a public variable named subtypeobj with type T::SubType.      
template <class T>
 class MyClass
 {
 public:
//add your public member here
}
           
//source.h
#include<iostream>
#include<string>
using namespace std;

class TypeA
{
public:
  class SubType
  {
  public:
    string toString() {return "subType in TypeA";};
  };
};

class TypeB
{
public:
  class SubType
  {
  public:
    string toString() {return "subType in TypeB";};
  };
};
 
template <class T>
class MyClass{
public:
   typename T::SubType subtypeobj;
};

           
//framework.cpp
#include<iostream>
#include<string>
#include"source.h"
using namespace std;


int main()
{
  MyClass<TypeB> obj2;
  cout << obj2.subtypeobj.toString() << endl;

  MyClass<TypeA> obj1;
  cout << obj1.subtypeobj.toString() << endl;

  MyClass<TypeA> obj3;
  cout << obj3.subtypeobj.toString() << endl;

  MyClass<TypeB> obj4;
  cout << obj4.subtypeobj.toString() << endl;

  return 0;
}

           
(4)[Template] Search a value
 Write a generic function int find_lower_bound(T seq[], int n, const T& value). The function returns the index of 
 the largest element in the given sequence that is less than the given value. If multiple elements satisfy, return 
 the one with smallest index. Return -1 if no such element exists.  
           
//source.h
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

template <typename T> 
int find_lower_bound(T seq[], int n, const T& value){
    int i = 0;
    T max;
    bool flag = 1;
	int max_index = -1; 
    for(i = 0;i < n;i++){
    	if(seq[i] < value && flag == 1){
    		flag = 0;
    		max = seq[i];
    		max_index = i;
		}
		if(seq[i] < value && max < seq[i]){
		    max = seq[i];
    		max_index = i;
		}
	} 
	
	return max_index;
}
           
//framework.cpp
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

#include "source.h"

struct Point{
	int x,y;
	bool operator<(const Point& p) {
		return (x<p.x || (x==p.x&&y<p.y));
	}
};

int main()
{
	//freopen("test01.in", "r", stdin);
	//freopen("test01.out", "w", stdout);


	{
	int A[5] = { 2, 10, 8, 6, 9 };
	cout<<find_lower_bound(A, 5, 9)<<endl;
	}
	{
	int A[5] = { 2, 10, 8, 2, 9 };
	cout<<find_lower_bound(A, 5, 3)<<endl;
	}
	{
	int A[5] = { 2, 1, 2, 6, 9 };
	cout<<find_lower_bound(A, 5, 1)<<endl;
	}

	double B[5] = { 3.0, 4.5, 6.0, 5.1, 1.3 };
	cout<<find_lower_bound(B, 5, 5.2)<<endl;

	Point C[5] = { {1,2},{2,3},{4,5},{1,3},{2,4}};
	Point c={3,2};
	cout<<find_lower_bound(C,5,c)<<endl;

	return 0;
}
           
(5)[Template] The Max of a sequence
Write a generic function T max(T seq[], int n) that returns the maximum element of a given sequence with n
 elements (n>=1). You are ensured the type T could be compared with operator '<'.
           
//source.h
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
template <typename T>
T max(T seq[], int n){
  int i = 0;
  T m = seq[0];
  for(i = 1;i < n;i++){
  	if(seq[i] > m){
  		m = seq[i];
	  }
  } 
  
  return m;	
}
           
//framework.cpp
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

#include "source.h"

int main()
{
	//freopen("test01.in", "r", stdin);
	//freopen("test01.out", "w", stdout);


	{
		char arr[] = "pqwridkfendkfnccsdsafsfsf";
		cout<<max(arr,strlen(arr))<<endl;
	}
	{
		double arr[] = { 2.1, 3.4, 5.6, 3.3, 5.1, 10.2 };
		cout<<max(arr,sizeof(arr)/sizeof(double))<<endl;
	}
	{
		int arr[] = { 2, 3, 5, 3, 5, 10, 1, 1005, 532 };
		cout<<max(arr,sizeof(arr)/sizeof(int))<<endl;
	}
	{
		string arr[3] = { "hello", "hi", "haha" };
		cout<<max(arr,3)<<endl;
	}

	return 0;
}
           
(6)[Template] The stack class 
 you are to implement the following Stack class template, using the nontype
  parameter capacity      to allocate the capacity of the stack, i.e.
   maximum elements that can be stored in the stack.

template<typename T, int capacity>
class Stack
{
public:
Stack();                 // Constructs an empty stack.
bool empty();         // Returns true if the stack is empty.
T peek();               // Returns the element at the top of the stack without removing it from the           stack.
void push(T value); // Stores an element into the top of the stack.
T pop();                 // Removes the element at the top of the stack and returns it.
int size();               // Returns the number of elements in the stack.
private:
T* elements;          // Points to an array that stores elements in the stack.
int num;                 // The number of the elements in the stack.
};
           
//source.h
template<typename T, int capacity>
class Stack
{
public:
    Stack();                 // Constructs an empty stack.
    bool empty();         // Returns true if the stack is empty.
    T peek();               // Returns the element at the top of the stack without removing it from the stack.
    void push(T value); // Stores an element into the top of the stack.
    T pop();                 // Removes the element at the top of the stack and returns it.
    int size();               // Returns the number of elements in the stack.
private:
    T* elements;          // Points to an array that stores elements in the stack.
    int num;                 // The number of the elements in the stack.
};


template<typename T, int capacity>
Stack<T,capacity>::Stack(){
	  elements = new T[capacity];
	  num = 0;
}                     // Constructs an empty stack.
template<typename T, int capacity>
bool Stack<T,capacity>::empty(){
	  if(num == 0){
		return true;
	  }
	  else{
		return false;
   	  }
}             // Returns true if the stack is empty.
template<typename T, int capacity>
T Stack<T,capacity>::peek(){
	 return elements[num - 1];
}                  // Returns the elements at the top of the stack without removing it from the stack.
template<typename T, int capacity>
void Stack<T,capacity>::push(T value){
	 elements[num++] = value;
}  // Stores an elements into the top of the stack.
template<typename T, int capacity>
T Stack<T,capacity>::pop(){
	 num--;
	 return elements[num];
}                   // Removes the elements at the top of the stack and returns it.
template<typename T, int capacity>
int Stack<T,capacity>::size(){
    return num;
}               // Returns the number of elementss in the stack. 
           
//framework.cpp
#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include "source.h"
using namespace std;
int main()
{
 //freopen("test01.in", "r", stdin);
 //freopen("test01.out", "w", stdout);

 Stack<int,15> intStack;
 for (int i = 0; i < 15; i++) intStack.push(i);

 cout<<intStack.size()<<endl;
 while (!intStack.empty()) {
  cout << intStack.peek() << " ";
  intStack.pop();
 }
 cout << endl;

 Stack<string,3> stringStack;
 stringStack.push("Chicago");
 stringStack.push("Denver");
 stringStack.push("London");

 while (!stringStack.empty())
  cout << stringStack.pop() << " ";
 cout << endl;

 return 0;
}
           
(7)[Template] Function printStack
 Rewrite the Stack class to add the printStack function as an instance function to display all the    elements in the stack, as follows:
template<typename T>
class Stack
{
public:
Stack();
bool empty();
T peek();
void push(T value);
T pop();
int getSize();
void printStack();
};
           
//source.h
#include <iostream>
using namespace std;
template<typename T>
class Stack
{
 public:
    Stack(){
    	elements = new T[100];
    	num = 0;
	}
    bool empty(){
    	if(num == 0){
    		return true;
		}
		else{
			return false;
		}
	}
    T peek(){
    	return elements[num - 1];
	}
    void push(T value){
    	elements[num++] = value;
	}
    T pop(){
    	num--;
    	return elements[num];
	}
    int getSize(){
    	return num;
	}
    void printStack(){
    	int i = 0;
    	for(i = num - 1;i >= 0;i--){
    		cout<<elements[i]<<endl;
		}
	}
 private:
 	T *elements;
 	int num;
}; 

           
//framework.cpp
#include <iostream>
#include"source.h"
using namespace std;
 

int main()
{

  Stack<int> s;
 
  for(int i=0;i<10;++i) s.push(i);
  s.printStack();

  cout << s.peek() << endl;
  s.printStack();

  s.pop();
  s.printStack();
  
  s.push(100);
  s.printStack();
  
  cout << "!" << endl;
  while (!s.empty())
  {
    cout << s.peek() << endl;
    s.pop();
  }
  
  return 0;

}

           

繼續閱讀