THUOOP icon indicating copy to clipboard operation
THUOOP copied to clipboard

auto,auto& 与auto*

Open liu-hz18 opened this issue 4 years ago • 2 comments

auto, auto* 与 auto&

by 刘泓尊 计84 [email protected]

c++98 auto

c++98标准中设置了auto关键字,与c++11的auto存在很大不同。c++98的auto用于声明变量为自动变量,拥有自动的生命周期。

int a =1;  //拥有自动生命期
auto int b = 2;  //拥有自动生命期
static int c = 3; //延长了生命期

C++98中的auto多余且极少使用,c++11中auto已经改为了“自动推断变量的类型”。

c++11 auto

1. 为变量自动选择匹配的类型

auto i = 100; //int
auto p = new MyClass(); // MyClass*
auto a = 123LL; //long long

但是上述做法也带来了缺陷,那就是降低了代码的可读性

但还存在一个权衡:当在单独的较小的代码区域内重复类名时,重复可能无助于提高可读性。

2. 代替冗长却又没必要写明的变量类型

std::vector<std::string> vec;
for(auto iter = vec.begin(); iter != vec.end(); iter ++){
	//do something...
}

在这里,auto代替了std::vector<std::string>::iterator上述循环在c++11中还可以简化为基于范围的for循环

for(auto iter : vec){
	//do something...
}
! ! ! 尤其是遍历/使用map时,特别推荐使用auto

因为很多人可能不知道map的value_type是std::pair<const KeyType, MappedType>而不是std::pair<KeyType, MappedType>,但是当你用后者时,编译是能通过的,因为存在隐式构造类型转换。但是转换后的对象就不再是map中存的那个。

for (const auto& item : some_map) {
  const KeyType& key = item.first;
  const ValType& value = item.second;
  // The rest of the loop can now just refer to key and value, a reader can see the types in question, and we've avoided the too-common case of extra copies in this iteration.
}

3. 用于声明依赖于模板参数的变量类型

template<class Tx, class Ty>
auto add(Tx x, Ty y) -> decltype(x + y){ //c++11
    return x + y;
}
template<class Tx, class Ty>
auto add(Tx x, Ty y){                   //c++14
    return x + y;
}

4. 配合lambda表达式

如果不使用auto, 那么一般需要使用std::function来存储。

auto closure = [](const int&, const int&){} //{lambda(int const&, int const&)#1}
auto closure = [](auto x, auto y){return x + y;} //c++14

//Sort `vec` in increasing order
std::sort(vec.begin(), vec.end(), [](auto lhs, auto rhs){
    return lhs > rhs; 
}));

5. 常见错误汇总

a. auto变量必须在定义时初始化
b. 对于多个auto变量的定义,这些变量必须为同一类型
auto a = 1, b = 2, c = 3; //correct
auto a = 1, b = "123"; //error: inconsistent deduction for 'auto': 'int' and then 'const char*'
c. auto初始化表达式是引用&时, 去除引用语义
int a = 1;
int &b = a;
auto c = b; //此时c的类型是int,不是int&

如果想令c的类型是引用,必须显式声明为auto&类型

auto& c = b;//此时c的类型是int&
d. 初始化表达式若为const/ volatile, 则去除const/ volatile(CV-qualifiers)语义

如有需要,则需要显式声明为const auto类型。

const int a = 10;
auto b = a; //b的类型为int,而不是const int
const auto c = a; //c的类型才为const int

注意:若auto关键字显式加上&符号,则不去除const语义

const int a = 10;
auto& d = a; //d的类型为const int
e. auto初始化表达式右值为数组时,auto推导为指针类型;auto&推导为数组类型。
int a[5] = {1, 2, 3, 4, 5};
auto b = a; //b类型为int*
auto &c = a; //c类型为int [5]

注意,指针≠数组

实际上,根据issue11[https://github.com/thu-coai/THUOOP/issues/11]中王世麟同学的分析,除以下三种情况,其他情况下数组都会退化成指向首元素的指针:

1. sizeof(s)
2. &s
3. "123"本身作为数组不退化

参考资料

《Google C++ Style Guide》,http://google.github.io/styleguide/cppguide.html

liu-hz18 avatar Mar 09 '20 11:03 liu-hz18

5.c中 auto*有类似表现 能够解释下吗

hzhwcmhf avatar Mar 22 '20 08:03 hzhwcmhf

update:

f. 对于加了代理(proxy)的对象,auto可能无法得到你想要的类型
std::vector<bool> vec_bool;
std::vector<int> vec_int;
vec_bool.push_back(false);
vec_int.push_back(0);

auto abool = vec_bool[0]; //std::_Bit_reference
auto aint = vec_int[0];   //int
auto temp = static_cast<bool>(abool);//bool

上例中,vector<bool>实际上加了一层代理类_Bit_reference。因为vector< bool>里面不是一个Byte一个Byte储存的,它是一个bit一个bit储存的。 可以通过static_cast<bool>来转换为bool类型。

g. auto* 的表现

auto*的表现比auto&情况简单很多,基本可以认为,如果赋值右侧为指针对象,那么左侧autoauto*无异。

int* arr_ptr1 = new int[5];
auto arr_ptr2 = arr_ptr1; //int*
auto* arr_ptr3 = arr_ptr1;//int*

int *ptr_arr[10];   //指针数组
auto ptr_arr1 = ptr_arr;  //int**

int (*arr_ptr)[10]; //数组指针
auto arr_ptr_copy = arr_ptr;//int (*)[10]
int* int_ptr1 = new int(10);
auto int_ptr2 = int_ptr1; //int*
auto int_ptr3 = int_ptr2; //int*
auto* int_ptr4 = int_ptr2;//int*
int var = 10;
auto var_ptr1 = &var;      //int*
auto var_ptr2 = var_ptr1;  //int*
auto* var_ptr3 = var_ptr1; //int*
h.关于字符串的类型推导
auto str1 = "123"; //char const*
auto& str2 = "123"; //char [4]
auto* str3 = "123"; //char const*

因为str1str3是指针,自然也就不能使用基于范围的for循环了。

liu-hz18 avatar Mar 22 '20 14:03 liu-hz18