三 auto占位符
3.1 重新定义的auto关键字
1.当用一个auto关键字声明多个变量的时候,编译器遵从由左往右的推导规则,以最左边的表达式推断auto的具体类型
int n = 5;
auto *pn = &n, m = 10;// 这里auto被推导为 int 所以int m = 10;合理
auto *pns = &n, m = 10.0;//编译失败,声明类型不统一
2.当使用条件表达式初始化auto声明的变量时,编译器总是使用表达能力更强的类型:
auto i = true ? 5 : 8.0; // i的数据类型为double
3.虽然C++11标准已经支持在声明成员变量时初始化(见第8章),但是auto却无法在这种情况下声明非静态成员变量
struct sometype {
auto i = 5; // 错误,无法编译通过
}struct sometype {
static const auto i = 5;
};//这个样子可以 但是i就是常量了 C++17修改
struct sometype {
static inline auto i = 5;
};
4.按照C++20之前的标准,无法在函数形参列表中使用auto声明形参(注意,在C++14中,auto可以为lambda表达式声明形参):
3.2 推导规则
1.在进行值传递的时候忽略原始的CV限定
const int i = 5; auto j = i; // auto推导类型为int,而非const int auto &m = i; // auto推导类型为const int,m推导类型为const int& auto *k = i; // auto推导类型为const int,k推导类型为const int* const auto n = j; // auto推导类型为int,n的类型为const int
2.使用auto声明变量初始化时,目标对象如果是引用,则引用属性会被忽略:
int i = 5; int &j = i; auto m = j; // auto推导类型为int,而非int&
3.使用auto和万能引用声明变量时(见第6章),对于左值会将auto推导为引用类型
int i = 5; auto&& m = i; // auto推导类型为int& (这里涉及引用折叠的概念) auto&& j = 5; // auto推导类型为int
根据规则3,因为i是一个左值,所以m的类型被推导为int&, auto被推导为int&,这其中用到了引用折叠的规则。而5是一个右值,因此j的类型被推导为int&&,auto被推导为int。
4.使用auto声明变量,如果目标对象是一个数组或者函数,则auto会被推导为对应的指针类型:
int i[5]; auto m = i; // auto推导类型为int* int sum(int a1, int a2) {return a1+a2; } auto j = sum // auto推导类型为int (__cdecl *)(int,int)
思考
class Base {
public:virtual void f(){std::cout << "Base::f()" << std::endl;};
};
class Derived : public Base {
public:virtual void f() override{std::cout << "Derived::f()" << std::endl;};
};Base* d = new Derived();auto& b = *d;//auto b = *d; b.f();
auto b 调用基类函数 auto& b调用子类的函数
个人认为的解释:*d的类型是确定的Base auto b = *d; 那边auto就是推导出来的Base 所以调用的就是Base的f函数
而 auto&b 编译器推导出变量的类型时,会保留右值表达式的引用性
右值 *d 的类型是 Base&,即 d 指向的 Derived 对象被解引用为 Base& 类型。
因此,b 的类型推导为 Base&(对 Base 的引用),实际引用的是 Derived 对象。所以会调用Derived的f函数
3.3 什么时候使用auto
1.当一眼能看出类型的时候使用auto
一般是在遍历容器的时候使用
如
vector<int> x{ 1,2,3 };for (vector<int>::iterator it = x.begin(); it != x.end(); ++it){}//等价于for (auto = x.begin(); it != x.end(); ++it){}//当使用map的时候 对于这个容器遍历前面的 string应该为coonststd::map<std::string, int> str2int;//这个可以不加constfor (map< std::string, int>::iterator it = str2int.begin(); it != str2int.end(); ++it){cout << it->second << endl;}//这个得加for (pair<const string,int> &it : str2int){cout << it.second << endl;}
2.用于lambda 与bind
auto l = [](int a1, int a2) { return a1 + a2; };
int sum(int a1, int a2) { return a1 + a2; };auto b = std::bind(sum, 5, std::placeholders::_1);
3.4 返回类型推导
C++14标准支持对返回类型声明为auto的推导
auto sum(int a1, int a2) { return a1 + a2; };
如果有多个返回值 要返回值的类型一致
不同的返回类型会导致编译失败。
3.5lambda表达式中使用auto类型推导
在C++14标准中我们还可以把auto写到lambda表达式的形参中,这样就得到了一个泛型的lambda表达式
auto l = [](auto a1, auto a2) { return a1 + a2; }; auto retval = l(5, 5.0); //在上面的代码中a1被推导为int类型,a2被推导为double类型,返回值retval被推导为double类型。
返回auto引用的方法
auto l = [](int &i)->auto& { return i; }; auto x1 = 5; auto &x2 = l(x1); assert(&x1 == &x2); // 有相同的内存地址
3.6 非类型模板形参占位符
c++17引入 它可以作为非类型模板形参的占位符
#include <iostream> template<auto N> void f() {std::cout << N << std::endl; } int main() {f<5>(); // N为int类型f<'c'>(); // N为char类型f<5.0>(); // 编译失败,模板参数不能为double }
c++17才有