typedef
typedef 官方的解释如下:
typedef - creates an alias that can be used anywhere in place of a (possibly complex) type name.
也就是说用来给类型名创建一个别名(alias),同时,typedef 可以出现在类型名的任何位置。
简单的别名创建
最简单的例子如下:
typedef unsigned long long ull; // unsigned typedef long long ull; 也可以
unsigned long long a = 1;
ull b = 1;
ull(c) = 1; // 和 ull b = 1; 一样
不仅 typedef 可以出现在类型名中的任何位置,类型名的顺序也可以是乱的:
// typedef can be used anywhere in the decl-specifier-seq
long unsigned typedef int long ullong;
// more conventionally spelled "typedef unsigned long long int ullong;"
a、b 的类型是一样,ull 明显比 unsigned long long 要简单很多。
数组名的别名创建
typedef 还可以用于数组名的别名创建,如下所示:
typedef int arr_t[10];
// the following two objects have the same type
int a1[10];
arr_t a2;
数组大小不能是变量,但可以是常量,比如 const int n = 10; 或者 constexpr int n = 10; 。
多个别名的创建
typedef 还可以用于多个别名的创建,用 逗号 分隔,如下所示:
typedef int int_t, *intp_t, arr_t[10];
int_t a; // int a;
intp_t b; // int* b;
arr_t c; // int c[10];
函数的别名创建
typedef 还可以用于函数别名的创建,如下:
int func(int a, int b) {
return a + b;
}
int main()
{
typedef int fp1(int, int), (&fp2)(int, int), (*fp3)(int, int);
fp1* f1 = func; // 函数名就是函数指针
fp2 f2 = func;
fp3 f3 = func;
auto res = func(1, 2);
auto res0 = (&func)(1, 2);
auto res1 = f1(1, 2);
auto res2 = f2(1, 2);
auto res3 = f3(1, 2);
printf("%d %d %d %d %d\n", res, res0, res1, res2, res3); // 3 3 3 3 3
reutrn 0;
}
上边几个函数定义看的眼花缭乱,但是意思大概就是,typedef int (函数); 定义了一个返回值是 int 类型的函数类型。因为函数名就是函数指针,所以如果是 typedef int fp1(int, int); 那就需要 fp1* f1 = func; 。参数列表需要一致。这样比较长的函数名就可以用简化的名字来调用了。值得注意的是,函数的调用,除了 func(1, 2); 竟然还可以 (&func)(1, 2); 但是不能 &func(1, 2); 。大神说,在windows 平台下用 min、max 函数,最好的方式是 (&std::max)(1, 2); 这样就不会被当做 <windows.h> 中宏定义的 max 函数,当然也可以 #define NOMINMAX 但是如果没有加,就会混淆。
下边的例子是用在类中的函数别名使用。
typedef void print_int(int);
typedef bool is_equal(const std::string& s1, const std::string& s2, size_t size, size_t& idx);
class X {
public:
static print_int(x_int); // static void x_int(int);
static is_equal(x_string); // static bool x_string(const std::string& s1, const std::string& s2, size_t size, size_t& idx);
};
void X::x_int(int a){
cout << a << endl;
}
bool X::x_string(const std::string& s1, const std::string& s2, size_t size, size_t& idx){
return false;
}
所以,比如看到一句: typedef Pointer (*CopyFunction)(const Pointer); 那可以用 CopyFunction 作为类型名,对函数进行创建。
用于模板
typedef 还可以用于模板,如下:
template< class T>
struct add_const {
typedef const T type;
};
typedef 可以和任何限定名一起使用,除了类型限定名。
The typedef specifier cannot be combined with any other specifier except for type-specifiers.
也就是说,下边这种写法是不行的:
typedef static unsigned int uint;
VS 会提示 “不能指定多个存储类”。
typename
typename 有三种用法:
1、告诉编译器,后边这个东西是个类型
2、在 typedef 中使用,用于告诉编译器,后边这个东西是个类型;
3、用于 template < typename T >,和 template < class T > 一模一样的作用。
// 1
template <class T>
void foo() {
typename T::iterator * iter;
// ...
}
// 2
typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;
// 3
template <typename T>
int compare(const T &v1, const T &v2) {
if (v1 < v2) return -1;
if (v2 < v1) return 1;
return 0;
}
第三种用法没什么好说的,主要看前两种用法。上边已经说了很多 typedef 的用法,没有看到 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.
就是说,对于用于模板定义的依赖于模板参数的名称,只有在实例化的参数中存在这个类型名,或者这个名称前使用了typename关键字来修饰,编译器才会将该名称当成是类型。除了以上这两种情况,绝不会被当成是类型。(https://blog.csdn.net/vanturman/article/details/80269081)
对于 T::iterator,T 类型下边的 iterator 不一定是个类型,有可能是个变量,如下:
template <class T>
void foo() {
T::iterator * ite;
// ...
}
struct A {
static int iterator
// ...
};
这样的话这个变量声明就变得很奇怪,变成了一个乘法。所以在VS中,T::iterator * ite; 这行会提示 "Dependent type requires using ‘typename’ keyword"。也就是需要改成 typename T::iterator * iter; 用于显式告诉编译器,T::iterator 是一个类型。
第二种用法也是这种意思,如果类型是依赖于模板参数的限定名,那么在它之前必须加 typename。其它情况下 typename 是可选的,也就是说对于一个不是依赖名的限定名,该名称是可选的,例如 vector vi;
不过在基类列表中,typename 可以不写: