📌 参考C++ primer 2.5
问题
在大型程序中,对象的类型往往十分复杂,例如,一个函数指针的类型,如下:
bool (*pf)(const string &, const int &, bool b &)
当作为一个整体放到函数中作为参数时,即冗长,不好理解,又容易出错,鉴于这种情况,我们需要一些解决办法。
类型别名 type alias
A type alias is a name that is a synonym for another type.
定义类型的别名,可以通过两种方式。
通过typedef定义
// 定义double的别名是wages
typedef double wages;
// 定义wages(double)的别名是base, 定义double *的别名是p;
// 相当于typedef wages base; typedef wages * p;
typedef wages base, *p;
wages i = 1.3;
p k = &i;
// 输出Pd, 代表pointer to double
cout << typeid(k).name() << endl;
通过using定义
The new standard introduced a second way to define a type alias, via an alias declaration.
- An alias declaration starts with the keyword using followed by the alias name and an =.
- The alias declaration defines the name on the left-hand side of the = as an alias for the type that appears on the right-hand side.
using SI = Sales_item;
SI si;
Pointer & const中使用的注意事项
当类型别名与复杂类型,如pointer等一起使用时,会出现一些难以理解的事情。
typedef char * pstring; // 定义pstring为指向char的指针类型, pointer to char
const pstring cstr = 0; // cstr is a constant pointer to char
const pstring *ps; // ps is a pointer to a constant pointer to char
在我们的使用,如果要判断具体的类型,会倾向于将类型替换为原始类型,如:
const pstring cstr = 0;
// 可以变成, 容易误解
const char *cstr = 0; // cstr is a pointer to const char
但是这种替换,导致了错误的理解,这里cstr不是pointer to const char
, 而是 const pointer to char
.
typedef char * pstring;
const pstring cstr = 0;
// 能修改
*cstr = 1;
char a = 'a';
// 错误,不能修改
cstr = &a;
cout << typeid(pstring).name() << endl;
// 输出:Pc 代表pointer to char
- 这里对于 *cstr = 1,没有报错,说明并不是
pointer to const char
; - 对cstr = &a报错,说明cstr本身是const,即
const pointer to char
;
这里进行类型替换理解时,需要将pstring整体考虑,即全部都是类型,不能将char * 拆开来看。可以这样理解,pstring是一个指针类型,这是正确的,将pstring视为一个类似int等的简单类型,因此 const pstring就是const pointer,不是pointer to const.
auto
当类型很复杂时,判断表达式的类型有时很困难,容易出错,C++11提出auto来告诉编译器推断表达式的类型。
基本使用
// item为int
auto item = 100;
// ok, int i, int *p
auto i = 0, *p = &i;
// error, int sz, double pi
auto sz = 0, pi = 3.14;
当在一个声明中,出现多个初始化操作时,要保证所有变量的base type是一致的。
复杂类型中auto的奇异表现
对于复杂类型,比如,指针、引用等,auto的结果并不一定是基本数据类型中那么直接,而是编译器会调整。
- 对于reference
int a = 0, &b = a; // c 为int, 不是 int& auto c = b; // 输出: i cout << typeid(c).name() << endl;
- 对于const
- 忽略 top-level const
- 保留 low-level const
因为,auto忽略了top-level const,如果想要保证变量为const,需要显式说明:int i = 0; const int ci = i, &cr = ci; auto b = ci; auto c = cr; auto d = &i; auto e = &ci; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; cout << typeid(e).name() << endl; // 输出 i: int i: int Pi: pointer to int PKi: pointer to const int
const auto f = ci;
- 对于自动推导类型的reference: 此时,top-level const不再忽略了,而是保留下来。
这里,跟typedef一样,使用const修饰auto或者typedef定义的类型时,const得到的是const reference(技术上不存在这个概念)或者 const pointer。// g 为reference to auto - int, ci的const被保留,因此为const int &, 不是 int或int & auto &g = ci; // error, 是字面量,无法取引用 auto &h = 42; // ok. j 为 const reference, 不是 reference to const int const auto &j = 42;
decltype
基本使用
使用auto时, a variable that uses auto as its type specifier must have an initializer. 但是,并不是每个变量都要初始化,比如函数返回值,此时auto无法使用,可以使用decltype
。
// 不会调用f(), 而是使用返回值类型作为sum的类型
decltype(f()) sum = x;
复杂类型下的表现
- decltype returns the type of that variable, including top-level const and references. 这一点与auto的表现不同。
const int ci = 0, &cj = ci; decltype(ci) x = 0; // x has type const int decltype(cj) y = x; // y has type const int& and is bound to x decltype(cj) z; // error: z is a reference and must be initialized
- Generally speaking, decltype returns a reference type for expressions that yield objects that can stand on the left-hand side of the assignment. 即,decltype推断表达式的类型时,当该表达式可以作为左值时,返回的是它的引用,不是原始类型。
int i = 42, * p = &i, &r = i; // r + 0 不能作为左值 decltype(r + 0) b; // ok: addition yields an int; b is an (uninitialized) int // *p 可以作为左值,接受赋值 decltype(*p) c; // error: c is int& and must be initialized
- Another important difference between decltype and auto is that the deduction done by decltype depends on the form of its given expression. 在decltype中使用()将变量包起来,会影响类型的推断。
// decltype of a parenthesized variable is always a reference decltype((i)) d; // error: d is int& and must be initialized decltype(i) e; // ok: e is an (uninitialized) int
📌 Remember that decltype((variable)) (note, double parentheses) is always a reference type, but decltype(variable) is a reference type only if variable is a reference.