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 表示什么

因为共有三种可能:

  1. 静态成员变量
  2. 静态成员函数
  3. 类内嵌套类型

加上 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关键字的更多相关文章

  1. typedef关键字

    1. typedef的作用 在计算机编程语言中用来为复杂的声明定义简单的别名,与宏定义有些差异.它本身是一种存储类的关键字,与auto.extern.static.register等关键字不能出现在同 ...

  2. C语言学习及应用笔记之五:C语言typedef关键字及其使用

    在C语言中有一个typedef关键字,其用来定义用户自定义类型.当然,并不是真的创造了一种数据类型,而是给已有的或者符合型的以及复杂的数据类型取一个我们自己更容易理解的别名.总之,可以使用typede ...

  3. C语言第四讲,typedef 关键字,以及作用域

    C语言第四讲,typedef 关键字,以及作用域 一丶typedef关键字 在C语言中,有typedef 关键字,这个关键字的作用就是允许你为类型定义一个新的名字,也就是 起个别的名字 例如: typ ...

  4. c++模板编程-typename与class关键字的区别

    最近一直在研究c++模板编程,虽然有些困难,但希望能够坚持下去.今天,在书上看见一个讨论模板编程typename与class两个关键字的区别,觉得挺有意义的,就把它们给总结一下. 先看一个例子: te ...

  5. C++模板之typename和class关键字的区别

    我们都知道,在STL中基本上都使用了模板类的声明,即template.在模板类的声明中,我们有两种方式: template <class T> template <typename ...

  6. c语言typedef关键字的理解

    1.typedef的定义 很多人认为typedef 是定义新的数据类型,这可能与这个关键字有关.本来嘛,type 是数据类型的意思:def(ine)是定义的意思,合起来就是定义数据类型啦. 不过很遗憾 ...

  7. 结构体 typedef关键字

    1 结构体 #include <iostream> #include <cstring> using namespace std; void printBook( struct ...

  8. C语言结构体及typedef关键字定义结构体别名和函数指针的应用

    结构体(struct)的初始化 struct autonlist { char *symbol; struct nlist nl[2]; struct autonlist *left, *right; ...

  9. 你好,C++(11)如何用string数据类型表示一串文字?根据初始值自动推断数据类型的auto关键字(C++ 11)

    3.5.2  字符串类型 使用char类型的变量我们可以表示单个字符,那么,我们又该如何表示拥有多个字符的字符串呢? 我们注意到,一个字符串是由多个字符串连起来形成的.很自然地,一种最简单直接的方法就 ...

随机推荐

  1. MySQL双主+keepalived实现高可用实现(热备)

    环境:centos6.7 最小化安装 192.168.100.152 master 主192.168.100.153 slave 从192.168.100.132 v_ip 浮动IP 配置ssh密码登 ...

  2. 行内元素(inline标签)设置了行高为什么不生效,还是表现为父盒子的行高?行内元素行高问题终极解释

    最近在看张鑫旭大佬的<css世界>,读到5.2.4  内联元素 line-height 的“大值特性” ,产生了疑惑, 在开发中确实也遇到了同样的问题,深入探究后得出结果,先说结论吧,论证 ...

  3. tabBarItem是模型,只有控件才有textColor属性

    如果通过模型设置控件的文字颜色,只能通过文本属性(富文本:颜色,字体,图文混排,空心)

  4. 用墨卡托和GPS坐标计算距离时误差测试

    iOS墨卡托和GPS坐标计算距离时误差测试,测试结果: 墨卡托和gps坐标来回转换没有误差. 墨卡托坐标计算出的距离比gps坐标计算出的距离大,100/92*100 = 108米,每100米多算出8米 ...

  5. 挑战10个最难的Java面试题(附答案)【下】【华为云技术分享】

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/devcloud/article/detai ...

  6. 区块链学习笔记:D04 区块链在各行业领域的应用(二)

    这节课主要是政务领域.版权存证领域.能源领域的应用案例介绍 1.房屋租赁联盟链 特点:真实可信.透明补贴.便于追溯.公共监督 节点:房屋运营节点.房管局节点.社保局节点.财政局节点.教育部门节点(多节 ...

  7. 使用Python为中秋节绘制一块美味的月饼

    每逢佳节- 对于在外的游子,每逢佳节倍思亲.而对于996ICU的苦逼程序猿们,最期待的莫过于各种节假日能把自己丢在床上好好休息一下了.这几天各公司都陆续开始发中秋礼品了.朋友圈各种秀高颜值的月饼,所以 ...

  8. virtualenv 在windows下的简单应用

    https://docs.python.org/zh-cn/3/tutorial/venv.html cmd下的操作: pip  install virtualenv pip install virt ...

  9. ios 在APP内提示更新

    http://www.jianshu.com/p/24daf5147bda     ios如何在应用内部提示更新  两颗星 http://www.jianshu.com/p/2ba10a58bb02  ...

  10. react-native中更改android/ios的入口文件

    android 1.android /app/build.gradle project.ext.react = [ entryFile: "index.android.js" ] ...