C++之typename和typedef关键字
1. typename的作用
template <typename Distance>
class KDTreeIndex : public NNIndex<Distance> {
public:
typedef typename Distance::ElementType ElementType;
typedef typename Distance::ResultType DistanceType;
typedef NNIndex<Distance> BaseClass;
typedef bool needs_kdtree_distance;
KDTreeIndex();
~KDTreeIndex(); private:
DistanceType* mean_;
DistanceType* var_;
};
对此处定义的模板类,调用时传入模版参数L2<T>,L2本身定义为模板结构体,相当于模板类
template<class T>
struct L2 {
typedef T ElementType;
typedef typename Accumulator<T>::Type ResultType; template <typename U, typename V>
inline ResultType accum_dist(const U& a, const V& b, int) const {
return (a-b)*(a-b);
}
};
其中调用的Accumulator定义如下
template<typename T>
struct Accumulator {
typedef T Type;
};
因此反向看回去, Accumulator<T>::Type 就是类型T, L2<T>::ElementType 也是类型T, L2<T>::ResultType 也是类型T,绕了一大圈结构 ElementType 和 ResultType 表示的还是最开始传入的L2<T>里面的T,只是从字面上更清晰地表示了出来。
typedef typename Distance::ElementType ElementType;
如果不加 typename 关键字,编译器就不知道 Distance::ElementType 表示什么
因为共有三种可能:
- 静态成员变量
- 静态成员函数
- 类内嵌套类型
加上 typename 就是告诉编译器这表示一个类型,从而消除了歧义。
2. 关键字 typedef 用法
// From C99 Standard
The typedef specifier is called a ‘‘storage-class specifier’’ for syntactic convenience only. In a parameter declaration, a single typedef name in parentheses is taken to be an abstract declarator that specifies a function with a single parameter,
not as redundant parentheses around the identifier for a declarator. In a declaration whose storage-class specifier is typedef, each declarator defines an identifier to be a typedef name that denotes the type specified for
the identifier in the way described as above.
Any array size expressions associated with variable length array declarators are evaluated each time the declaration of the typedef name is reached in the
order of execution. A typedef declaration does not introduce a new type, only a synonym for the type so specified.
2.1 为各种数据类型定义新名字
typedef type_name type_alias;
有点像 #define 宏定义,但宏定义是在预处理阶段进行直接替换,而 typedef 作用于编译期,可以用于超越预处理器处理能力的类型替换。简化代码,增强可读性,同时提高跨平台适应性。
正如C语言参考手册所言,任何 declarator 中的 identifier 定义为 typedef-name , 其表示的类型是 declarator 为正常变量声明的那个标识符的类型。举几个例子
// Example 1
int *p; // p是一个变量,其类型为pointer to int
typedef int *p; // 在int *p前面加typedef后,p变为一个typedef-name,这个typedef-name所表示的类型就是int *p声明式中p的类型(int*)。也即是说typedef去除了p普通变量的身份,使其变成了p的类型的一个typedef-name // Example 2
double MYDOUBLE; // 正常变量声明,声明一个变量MYDOUBLE,类型为double
typedef double MYDOUBLE; // MYDOUBLE是类型double的一个typedef-name
MYDOUBLE d; // d是一个double类型的变量 // Example 3
double *Dp; // 声明变量Dp,类型为double*,即pointer to double
typedef double *Dp; // Dp是类型double*的一个typedef-name
Dp dptr; // dptr是一个pointer to double的变量
对于复杂数据类型也是一样,比如结构体或者类
// Example 4
struct _Foo_t Foo_t; // 变量Foo_t的类型为struct _Foo_t
typedef struct _Foo_t Foo_t; // Foo_t是"struct _Foo_t"的一个typedef-name
Foo_t ft; // ft is a struct type variable // Example 5
struct { ... // } Foo_t; // 变量Foo_t的类型为struct { ... // }
typedef struct { ... // } Foo_t; // Foo_t是struct { ... // }的一个typedef-name, 这里struct {...//}是一个无"标志名称(tag name)"的结构体声明
或者一些数组名或指针的别名
// Example 6
int A[]; // 变量A的类型为一个含有5个元素的整型数组
typedef int A[]; // A是含有5个元素的数组类型的一个typedef-name
A a = {, , , , }; // Right
A b = {, , , , , }; // Warning: excess elements in array initializer // Example 7
// typedef int (*A)[5]; vs typedef int* A[5];
int (*A)[]; // 变量A的类型为pointer to an array with 5 int elements
typedef int (*A)[]; // A是"pointer to an array with 5 int elements"的一个typedef-name
int c[] = {, , , , };
A a = &c; // Right
printf("%d\n", (*a)[]); // output 3 int c[] = {, , , , , };
A a = &c; // Warning: initialization from incompatible pointer type
2.2 用来定义函数指针
分析方法与定义基本类型别名类似,都是去掉 typedef 后观察期类型
// Example 1
int* Func(int); // 变量Func的类型为一个函数标识符,该函数返回值类型为int*,参数类型为int
typedef int* Func(int); // Func是函数类型(函数返回值类型为int*,参数类型为int)的一个typedef-name
Func *fptr; // fptr是一个pointer to function with one int parameter, returning a pointer to int
Func f; // 这样的声明没有多大意义// Example 2
int (*PFunc)(int); // 变量PFunc的类型为一个函数指针,指向的返回值类型为int,参数类型为int的函数原型 typedef int (*PFunc)(int); // PFunc是函数指针类型(该指针类型指向返回值类型为int,参数类型为int的函数)的一个typedef-name
PFunc fptr; // fptr是一个pointer to function with one int parameter, returning int // Example 3
#include <iostream> int add(int a, int b) { return (a+b); }
typedef int (* func)(int , int ); int main(int argc, char *argv[]) {
func f = add; // 定义一个指针变量f,它是一个指向某种函数的指针,这种函数参数是两个int类型,返回值也是int类型。将其指向add函数入口地址。
int n = f(,); // 我们要从指针的层次上理解函数-函数的函数名实际上就是一个指针,函数名指向该函数的代码在内存中的首地址。
std::cout << n << std::endl; return ;
}// Example 4
#include <stdio.h> typedef int (*FP_CALC)(int, int); // 注意这里不是函数声明而是函数定义,它是一个地址,你可以输出add看看
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int mul(int a, int b) { return a * b; }
int div(int a, int b) { return b? a/b : -; } //定义一个函数,参数为op,返回一个指针。该指针类型为拥有两个int参数、返回类型为int 的函数指针。它的作用是根据操作符返回相应函数的地址
FP_CALC calc_func(char op) {
switch (op) {
case '+': return add;//返回函数的地址
case '-': return sub;
case '*': return mul;
case '/': return div;
default: return NULL;
}
return NULL;
} //s_calc_func为函数,它的参数是 op, 返回值为一个拥有 两个int参数、返回类型为int 的函数指针
int (*s_calc_func(char op)) (int, int) { return calc_func(op); } //最终用户直接调用的函数,该函数接收两个int整数,和一个算术运算符,返回两数的运算结果
int calc(int a, int b, char op) {
FP_CALC fp = calc_func(op); //根据预算符得到各种运算的函数的地址
int (*s_fp)(int, int) = s_calc_func(op);//用于测试
// ASSERT(fp == s_fp); // 可以断言这俩是相等的
if (fp)
return fp(a, b);//根据上一步得到的函数的地址调用相应函数,并返回结果
else
return -;
} int main(int argc, char *argv[]) {
int a = , b = ; printf("calc(%d, %d, %c) = %d\n", a, b, '+', calc(a, b, '+'));
printf("calc(%d, %d, %c) = %d\n", a, b, '-', calc(a, b, '-'));
printf("calc(%d, %d, %c) = %d\n", a, b, '*', calc(a, b, '*'));
printf("calc(%d, %d, %c) = %d\n", a, b, '/', calc(a, b, '/'));
return ;
}
3. 关键字 typename 用法
3.1. 在模板定义中,表明其后的模板参数为类型参数
template<typename T>
class Mytest {
public:
template<typename U, typename V>
T foo(const U &u, const V &v) {
// function body
} private
T t;
};
这里 typename 就相当于关键字 class ,二者可以相互替换,最初定义模板的方式就是 template<class T> ... 这样可以减少关键字的引入。
3.2 在模板中用于表明内嵌依赖类型名(Nested Dependent Type Name)
template<class _InputIter, class _Tp>
typename iterator_traits<_InputIter>::difference_type
count(_InputIter __first, _InputIter __last, const _Tp& __value) {
__STL_REQUIRES(_InputIter, _InputIterator);
__STL_REQUIRES(typename iterator_traits<_InputIter>::value_type, _EqualityComparable);
__STL_REQUIRES(_Tp, _EqualityComparable);
typename iterator_traits<_InputIter>::difference_type __n = ; for ( ; __first != __last; ++__first) {
if (*__first == __value) {
++__n;
}
} return __n;
}
这里有三处用到了 typename 关键字
typename iterator_traits<_InputIter>::difference_type
typename iterator_traits<_InputIter>::value_type
typename iterator_traits<_InputIter>::difference_type __n = ;
difference_type, value_type 就是依赖于 _InputIter (模板类型参数)的类型名。
iterator_traits 结构体定义如下:
// iterator_traits结构体定义
template <class _Iterator>
struct iterator_traits {
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
- 内嵌是指定义在类名的定义中的。比如这里的 difference_type, value_type 。
- 依赖是指依赖于一个模板参数。比如difference_type (typename iterator_traits<_inputiter>::difference_type) 依赖于模板参数 _InputIter 。
- 类型名是指最终要指出的是个类型名,而不是变量。比如 iterator_traits<_inputiter>::difference_type 完全有可能是类 iterator_traits<_inputiter> 类里的一个 static 对象。并且C++默认就是解释为一个变量。所以为了避免歧义,使用 typename 告诉编译器。
但这并不说所有的 T::type_or_variable , 或者 tmpl:type_or_variable 都需要使用 typename ,比如下面的情况
// The first situation
template<class T>
class Derived: public Base<T>::XXX
{
// ...
} // Another situation
Derived(int x) : Base<T>::xxx(x)
{
// ...
}
第一种是类模板定义中的基类列表,里面肯定是类型名,第二种是类模板定义中的初始化列表,里面肯定是成员变量,这对于编译器而言没有任何歧义,因此不需要 typename 关键字。
C++之typename和typedef关键字的更多相关文章
- typedef关键字
1. typedef的作用 在计算机编程语言中用来为复杂的声明定义简单的别名,与宏定义有些差异.它本身是一种存储类的关键字,与auto.extern.static.register等关键字不能出现在同 ...
- C语言学习及应用笔记之五:C语言typedef关键字及其使用
在C语言中有一个typedef关键字,其用来定义用户自定义类型.当然,并不是真的创造了一种数据类型,而是给已有的或者符合型的以及复杂的数据类型取一个我们自己更容易理解的别名.总之,可以使用typede ...
- C语言第四讲,typedef 关键字,以及作用域
C语言第四讲,typedef 关键字,以及作用域 一丶typedef关键字 在C语言中,有typedef 关键字,这个关键字的作用就是允许你为类型定义一个新的名字,也就是 起个别的名字 例如: typ ...
- c++模板编程-typename与class关键字的区别
最近一直在研究c++模板编程,虽然有些困难,但希望能够坚持下去.今天,在书上看见一个讨论模板编程typename与class两个关键字的区别,觉得挺有意义的,就把它们给总结一下. 先看一个例子: te ...
- C++模板之typename和class关键字的区别
我们都知道,在STL中基本上都使用了模板类的声明,即template.在模板类的声明中,我们有两种方式: template <class T> template <typename ...
- c语言typedef关键字的理解
1.typedef的定义 很多人认为typedef 是定义新的数据类型,这可能与这个关键字有关.本来嘛,type 是数据类型的意思:def(ine)是定义的意思,合起来就是定义数据类型啦. 不过很遗憾 ...
- 结构体 typedef关键字
1 结构体 #include <iostream> #include <cstring> using namespace std; void printBook( struct ...
- C语言结构体及typedef关键字定义结构体别名和函数指针的应用
结构体(struct)的初始化 struct autonlist { char *symbol; struct nlist nl[2]; struct autonlist *left, *right; ...
- 你好,C++(11)如何用string数据类型表示一串文字?根据初始值自动推断数据类型的auto关键字(C++ 11)
3.5.2 字符串类型 使用char类型的变量我们可以表示单个字符,那么,我们又该如何表示拥有多个字符的字符串呢? 我们注意到,一个字符串是由多个字符串连起来形成的.很自然地,一种最简单直接的方法就 ...
随机推荐
- 串的匹配算法--C语言实现
串这种数据结构,使用是比较多的,但是它的一些方法在更高级的语言中,比如Java,Python中封装的比较完整了.在这里,我只写了串中使用最多的匹配算法,即串的定位操作.串的匹配算法常用的两种就是朴素匹 ...
- 【nodejs原理&源码赏析(2)】KOA中间件的基本运作原理
[摘要] KOA中间件的基本运作原理 示例代码托管在:http://www.github.com/dashnowords/blogs 在中间件系统的实现上,KOA中间件通过async/await来在不 ...
- 【开发者portal在线开发插件系列三】字符串 及 可变长度字符串
基础篇 基础场景见上面两个帖子,这里单独说明字符串和可变长度字符串的用法. 话不多说,开始今天的演(表)示(演) Profile和插件开发 添加一个string类型的属性: 在插件里添加一条数据上报消 ...
- ios宏定义应该呆在恰当的地方
项目为了看起来整洁 并减少不必要的多次拼写 我们会把这样的方法 做成宏定义 那么问题来了 很多文件同时用到一个或多个宏定义 写完之后就会变成这个样子 看起来很乱 阅读性也不好 那么问题来了怎么解决嘞 ...
- java概述和java环境按照,java开发体验
java概述: Java的发展可以归纳如下的几个阶段. (1)第一阶段(完善期):JDK 1.0 ( 1995年推出)一JDK 1.2 (1998年推出,Java更名为Java 2): (2)第二阶段 ...
- Python3 猜年龄小游戏进阶之函数处理
在猜年龄的基础上编写登录.注册方法,并且把猜年龄游戏分函数处理 登录函数 注册函数 猜年龄函数 选择奖品函数 # 注册 def register(): '''注册''' count = 0 while ...
- redis(7)--redis应用实战
问题1:哨兵模式下客户端应该连接哪个redis-server? 问题2:集群模式下为什么会有MOVED error Redis Java客户端介绍 已有的客户端支持 Redis Java客户端有很多的 ...
- 带你快速了解Java锁中的公平锁与非公平锁
前言 Java语言中有许多原生线程安全的数据结构,比如ArrayBlockingQueue.CopyOnWriteArrayList.LinkedBlockingQueue,它们线程安全的实现方式并非 ...
- SOCKET CAN的理解
转载请注明出处:http://blog.csdn.net/Righthek 谢谢! CAN总线原理 由于Socket CAN涉及到CAN总线协议.套接字.Linux网络设备驱动等.因此,为了能够全面地 ...
- NodeJS3-2基础API----Buffer(缓冲器)
Buffer(缓冲器) Buffer是用于处理二进制数据流的 实例类似整数数组,大小固定(实例化之后,是多大就多大,不能进行变更) C++代码在V8 对外分配物理内存 Buffer是全局变量,没必要使 ...