天天看点

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;

}

           

继续阅读