您的位置:首页 > 编程语言 > C语言/C++

C++点滴--auto

2018-01-10 18:27 441 查看

1. 类型推导规则

1.1 auto

auto a = {expression};


如果{expression}是一个引用,则引用修饰符(&)被忽略;

如果{expression}有top-level的const/volatile修饰符,则const/volatile被忽略;

例子:

int i = 0;
const int & cri = x;
auto a = cri;   // type of a is "int", because the reference and the const are ignored;

int x = 42;
const int* p1 = &x;
auto p2 = p1;   // const is not ignored, because it's not at top-level;
*p2 = 43;       // error: p2 is const int*


总结:

去引用;

const能去则去(需要保证const变量不能被修改);

1.2 auto &

auto& ra = {expression};


引用修饰符(&)不能被忽略;

const修饰符也不能被忽略(因为引用保留了,若忽略const,则常量表达式能被修改了);

例子:

const int c = 0;
auto& rc = c;  //type of rc is "const int &", const is not ignored; otherwise, c can be modified;
rc = 1;        //error;


总结:

保留引用;

const不能去(需要保证const变量不能被修改);

1.3 auto &&

auto&& rra = {expression};


如果{expression}是左值,则需要添加左值引用&,所以rra的类型变成T& &&,衰减(collapse)成T&;

如果{expression}是右值,则rra的类型是T&&;

例子:

int i = 42;
auto&& ri_1 = i;    //type of ri_1 is "int& &&", collapsing to "int &";
auto&& ri_2 = 42;   //type of ri_2 is "int &&";


总结:

左值,左值引用;

右值,右值引用;

const能去则去(需要保证const变量不能被修改);

2. 使用场景

2.1 基础用法

std::vector<int> vect;
// ...
for(std::vector<int>::iterator it = vect.begin(); it != vect.end(); ++it)
{
std::cin >> *it;
}


可以更简便的写作:

std::vector<int> vect;
// ...
for(auto it = vect.begin(); it != vect.end(); ++it)
{
std::cin >> *it;
}


变量it和vect.begin()的类型是完全一样的,因此,编译器可以推断出它的类型,不必麻烦程序员每次重写一遍。还有其他类似的使用方式,其目的是让编译器来推断类型,程序员省去一些打字的工作(毕竟,写个auto是很快捷的)。

然而,本人不喜欢这样的用法,理由是(本人的拙见,欢迎持不同意见的同学来讨论):

打字在编程中占的工作量有限,在这方面节省一点时间,能提高多少效率呢?

程序员在写代码的时候,实际上是很清楚每个变量的类型的,这个时候为了节省一点打字工作而简单的写个auto,维护(阅读)代码的人可能需要自己去推导变量的类型。而这个推导成本,应该远远大于打字节省的工作量吧!

即使对于上面的例子(for循环遍历vector),明确的写出vector< int >::iterator(或者vector< int >::const_iterator),也可以让程序员熟悉vector的使用方式,甚至能够使程序员联想起vector以及iterator的实现(类似的,map及其iterator的实现……)。

所以,本人认为,仅仅为了编码便利,我们不应该使用auto;它应该出现在有正当需求的地方,见下文。

2.2 无法确定类型的地方

比如,我们写这样一个模板函数:

template<typename T, typename S>
void foo(T lhs, S rhs) {
auto prod = lhs * rhs;
//...
}


C++11以前,表达式lhs*rhs的类型无法确定(若lhs都是int,则为int;若一个int一个double则为double;另外,T和S还可能是程序员自定义的类——重载了乘法运算符——那么结果类型就有无限多可能了),我们该如何写prod的类型呢?

C++11有两种途径来定义prod的类型,其一就是如上所示的auto。另一个是decltype,我在这一篇里介绍。

2.3 Function return type deduction (C++14)

C++11中提供了一个特性:lambda函数的类型可以由return表达式的类型推定。C++14把这一特性扩展到所有函数:也就是,函数的返回类型定义为auto (注意,后面没有 -> decltype(),这是C++11的trailing return type语法)。例如:

auto add(int a, int b)
{
return a + b;
}


和2.1节类似,编译器可以根据return语句推断出函数的类型。同样,我不喜欢这样的写法,理由也类似。除此之外,它还有一些限制:

限制1: 若有多个返回语句,则各个返回表达式的类型必须一样;

限制2:可以前置声明,但是定义之前是不能使用的(那前置声明有什么用呢?);

限制3: 返回auto的函数递归时, 递归调用前,至少有一个return 语句。例如:

auto Correct(int i) {
if (i == 1)
return i;               // return type deduced as int
else
return Correct(i-1)+i;  // ok to call it now
}

auto Wrong(int i) {
if (i != 1)
return Wrong(i-1)+i;  // Too soon to call this. No prior return statement.
else
return i;             // return type deduced as int
}


2.4 trailing return type

这是decltype的使用方式,auto只是配合一下。见我的另一篇博客。

2.5 Alternate type deduction on declaration

这是auto和decltype结合的使用方式。见我的另一篇博客。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: