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类型的变量我们可以表示单个字符,那么,我们又该如何表示拥有多个字符的字符串呢? 我们注意到,一个字符串是由多个字符串连起来形成的.很自然地,一种最简单直接的方法就 ...
随机推荐
- 前端vue如何下载或者导出word文件和excel文件
前端用vue怎么接收并导出文件 window.location.href = "excel地址" 如果是 get 请求,那直接换成 window.open(url) 就行了 创建一 ...
- 这事没完,继续聊spring cloud stream和kafka的这些小事
上一篇文章讲了如何用spring cloud stream集成kafka,并且跑起来一个demo,如果这一次宣传spring cloud stream的文章,其实到这里就可以啦.但实际上,工程永远不是 ...
- HTML 5 中的textarea标签
HTML 5 中的textarea标签 定义和用法 定义一个文本区域 (text-area) (一个多行的文本输入区域).用户可在此文本区域中写文本.在一个文本区中,您可输入无限数量的文本.文本区中的 ...
- Nginx配置详细解析(全)
一.nginx.conf文件结构 (1)共三部分:由全局块.events块.http块组成.http块又包含http全局块.server块:server块由多个location块组成. (2)一般情况 ...
- LeetCode 5282. 转化为全零矩阵的最少反转次数
地址 https://leetcode-cn.com/submissions/detail/39277402/ 题目描述给你一个 m x n 的二进制矩阵 mat. 每一步,你可以选择一个单元格并将它 ...
- TestNG系列(二)TestNG注解
前言 TetsNG提供了很多注解,允许测试人员灵活地组织测试用例 一.@Test @Tets是TestNG的核心注解,被注解的方法,表示为一个测试方法. description属性 @Test(des ...
- luogu P2740 [USACO4.2]草地排水Drainage Ditches |网络流
题目背景 在农夫约翰的农场上,每逢下雨,贝茜最喜欢的三叶草地就积聚了一潭水.这意味着草地被水淹没了,并且小草要继续生长还要花相当长一段时间.因此,农夫约翰修建了一套排水系统来使贝茜的草地免除被大水淹没 ...
- Java修炼——容器HashMap用法
直接上代码,容器集合之间的关系在后面我会继续详细分析,这次先看HashMap用法 HashMap的方法都在代码中有解释.有需要的可以仔细看看 package com.bjsxt.map; import ...
- [TimLinux] myblog 页面Axure设计
1. 导航 2. 首页主体 3. 侧边栏 4. 页尾 5. 使用工具 Axure RP 8.0.0.3312 Pro版本.
- 2019 ICPC南昌邀请赛网络赛比赛过程及题解
解题过程 中午吃饭比较晚,到机房lfw开始发各队的账号密码,byf开始读D题,shl电脑卡的要死,启动中...然后听到谁说A题过了好多,然后shl让blf读A题,A题blf一下就A了.然后lfw读完M ...