类型推断

C++98有一个关于类型推断的简单规定集:函数模板。C++11修改一部分规定集,增加两部分:auto和decltype。C++14在auto和decltype进行了扩展。你在拼写受到明的限制时,自由类型推断的广泛应用。在源文件某处更改一个类型能自动类型推断适应到其他位置,使得C++软件更加适应。但是,由于类型推断对于编译器可能不是你想要的结果,得到的代码更加难。

没有完全明白类型推断怎样执行,在现代C++编程中进行有效编程难实现。对于描述类型推断有大量的资料:函数模板,大多情况下出现auto,在decltype表达式中,比如C++14中,令人困惑的decltype或auto结构给使用。

本章提供每个C++开发者需要的类型推断信息。阐明模板类型推断怎么运行,auto和decltype怎样以自己的方式进行编译。好的编译器将展现类型推断的结果,让你知道编译器能得到你想要的结果。

条款1:理解模板类型推断

当使用者对复杂系统怎么运行进行忽视时,对于系统的设计能做什么感到庆幸。总而言之,模板类型推断在C++中获得了巨大的成功。大量程序员对通过参数实现泛型函数的结果感到满意,即使大量程序员对于函数怎样推断感到困惑。

如果这个群体里面有你,你将获得两个消息-好消息和坏消息。好消息是模板类型推断是基于现代C++最有力的特征:auto。如果你对于C+98中模板的类型诊断感到庆幸,你将对C++11的auto类型推断感到高兴。坏消息是当模板类型推断被应用于auto中,有时不能值观获得模板类型。因此,对于理解生成auto类型推断是相当重要的。本条款增加你的了解。

如果你想看伪代码,你可以考虑这样的函数模板:

template<typename T>
void f(paramType param);

可以这样调用函数:

f(expr);

通过编译,编译器使用expr推断出两种类型:一种是T,一种是paramType。他们的类型通常是不同的,因为paramType包含修饰词,例如,const或引用。如果模板是这样声明的。

template<typename T>
void f(const T& param)

我们可以这样调用:

int x = 0;
f(x);

T被推断出int类型,但是paramType被推断出const int&类型。

通常期望函数可以推断出T类型为参数类型,比如,根据expr推出T的类型,在上面示例中,x是int类型,T被推断为int,但并不总是这样实现。类型推断不仅仅依赖表达式的类型,还依赖参数的形式。在以下情形中:

参数类型是一个指针或引用类型,而不是普通引用。
参数类型是一个通用引用;
参数类型既不是指针也不是引用。

我们有三种类型推断检查,每一种都基于通用形式模板并调用:

template<typename T>
void f(paramType param);

f(expr);

情形1:参数类型是指针或引用,而非普通引用

最简单情形是参数类型是引用类型或指针类型,而非普通引用。这种情况,类型推断像这样执行:

1.如果表达式是一个引用,忽略引用部分。
2.模式匹配表达式类型取决于T。

示例,如果这是模板:

template<typename T>
void f(T& param);

且有变量声明:

int x = 27;
const int cx = x;
cosnt int& rx = x;

推断类型param和T在调用中实现:

f(x);

f(cx);

f(rx);

在后面两种调用中,注意到cx和rx不同于const值,T被推断为const int,因此生成参数类型为const int&。这是重要的调用。当将一个常量对象传递给引用参数,他们期望对象保留不可修改。比如:传递参数常量引用,这就是为什么通过将常量对象传递给一个模板采用T&是安全的:常量对象成为类型推断的一部分。

在最后一个示例中,通过rx类型传给一个引用,T被推断为非引用。这是因为re的引用性在推断期间被忽略。

这三个示例均为左值引用参数,但是类型推断以同样的方式运用于右值引用。当然,只有右值参数可以传递给右值参数,对于类型推断没有任何限制。

如果我们改变函数参数类型从T&变为const T&,虽然有所改变,但是实际上没有任何改变。常量cx和rx没有没有变成const 类型。

template<typename T>
void f(const T& param);

int x =27;
const int cx = x;
const int& rx = x;

f(x);

f(cx);

f(rx);

如前所述,rx的引用在推断期间被忽略。

如果参数是一个指针而非引用,类型推断以同样的方式运用:

template<typename T>
void f(T* param);

int x = 27;
const int *px = &x;

f(&x);

f(px);

现在,你可能乏味,因为C++类型推断规则运用在指针和引用参数中,书写的方式是沉闷的。一切如此明显。哪有完全如你所愿。

情形2:参数类型是普通引用

类型推断在模板采用普通引用中不是那么明显。比如参数被用右值声明,但他们与右值参数被传递有所不同。具体之处在条款24中说明,这是头版:

如果表达式是右值,T和typeparam被推断为右值引用。仅有在这种情形下被推断为引用,其次,paramtype被声明作为右值语义,推断为右值引用。

如果表达式是一个右值,按照正常情形应用。

示例:

template<typename T>
void f(T&& param);

int x = 27;

const int cx = x;

const int& rx = x;

f(x);

f(cx);

f(rx);
f(27);

条款24会解释这些示例为什么是这样。关键在于普通引用的类型推断规则不同于右值引用参数或右值引用。尤其是,当普通引用被使用时,类型推断不同于右值参数。这种情况不会在非普通引用中出现。

情形3:参数类型既不是指针亦非引用

当参数类型既不是指针亦不是引用时,我们通过值传递参数进行处理:

template<typename T>
void f(T param);

者意味着参数在任何情形下会以一个对象的副本传递。实际上param将以一个新的对象有目标性推断出表达式的类型:

1.在此之前,如果表达式是一个引用,忽略引用部分。
2.如果表达式是const,忽略常量引用部分,如果是可变,也忽略。

因此:

int x = 27;
const int cx = x;
cosnt int rx& = x;

f(x);

f(cx);

f(rx);

注意即使rx和cx是const值,参数不是常量的,参数的意义在于它是一个对象,它完全独立于cx和rx-作为cx和rx的副本。实际上cx和rx无论在任何情况下不会被参数修改。这就是表达式常量部分被忽略的原因。不能因为表达式不能被修改并不意味着不能被复制。

意识到仅在通过值传递参数时const被忽略势必要的。参数通过引用或指针,表达式的保留类型推断,考虑到表达式是一个常量指针指向常量对象,expr通过值传递参数:

template<typename T>
void f(T param);

const char* const ptr = "Fun with pointer";

f(ptr);

猜你喜欢

转载自blog.csdn.net/qq_37774304/article/details/85872358