重新定义的auto关键字(C++11)
在C++98标准中的auto是用来声明自动变量的,简单来说就是拥有自动变量的生命周期,显然这是多余的,几乎很少用到它。于是在C++11中赋予了auto新的含义:声明变量时根据初始化表达式自动推断该变量的类型、声明函数时函数返回值的占位符。例如:
1)................................................................................
/*当使用条件表达式初始化auto声明的变量时,编译器总是使用表达能力更强的类型,因此下面的i会被
推导为double类型*/
auto i = true ? 5 : 8.0;
2)................................................................................
/*如果auto声明的变量是按值初始化,则推导出的类型会忽略cv限定符(const和volatile)。进一步解
释为,在使用auto声明变量时,既没有使用引用也没有使用指针,那么编译器在推导的时候会忽略cv限
定符。当然auto本身也支持添加cv限定符:*/
const int i = 5;
auto j = i; //按值初始化,auto类型推导为int,而非const int
auto &m = i; //按引用初始化,auto类型推导为const int, m推导类型为const int&
auto *m = &i; //按指针初始化,auto类型推导为const int, m推导类型为const int*
const auto n = j; //const和auto可以结合使用,auto推导类型为为int, n的类型为const int
3)................................................................................
class Base {
public:
virtual void fun() {std::cout << "Base::fun" << std::endl;};
};
class Derived : public Base {
public:
virtual void fun() override {std::cout << "Deriverd::fun" << std::endl;};
};
Base* d = new Derived();
/*下面的语句是按值赋值,因此auto直接推导为Base。代码自然会调用Base的复制构造函数,也就是
说Derived被切割成了Base, 这里的b.fun()最终调用Base的fun函数*/
auto b = *d;
b.fun();
4)................................................................................
/*使用auto声明变量初始化时,目标对象如果是引用,则引用属性会被忽略:*/
int i = 5
int &j = i;
auto m = j; //auto推导类型为int, 而非int&
5)................................................................................
/*使用auto和万能引用声明变量时,对于左值会将auto推导为引用类型。当右值引用&&与类型推导
结合在一起时,将变得非常灵活。它既可以与左值绑定也可以与右值绑定,此时它变成一种通用引用类
型(universal reference)*/
int i = 5;
auto&& m = i; //auto推导类型为int&
auto&& j = 5; //auto推导为int
6)................................................................................
/*需要注意的是,如果使用auto声明变量的,则会导致其他程序员阅读代码时需要翻阅初始化变量的具体
类型,那么我们就需要慎重考虑是否适合使用auto关键字。不过在类型比较复杂的场景,比如在容器的迭
代器上、lambda表达式、bind等直接使用auto。*/
/*示例1*/
std::map str2int;
//...填充str2int的代码
/*下面的auto被推导为std::map::const_iterator*/
for(auto it = str2int.cbegin(); it != string2int.cend(); ++it) {}
//或者
/*下面的auto被推导为std::pair*/
for(auto &it : str2int) {}
/*示例2*/
/*这里的1可能是xxx::,不同的编译器命名方法可能不同,我们根本无法写出
其类型,只能用auto来声明*/
auto 1 = [](int a1, int a2) {return a1 + a2}
/*示例3*/
int sum(int a1, int a2) {return a1 + a2}
/*std::_Binder &>便
是b推导的类型,太复杂了,所以直接使用auto*/
auto b = std::bind(sum, 5, std::placeholders::_1);
7)................................................................................
/*sum函数中的auto是一个返回值占位符,真正的返回类型是int。这里sum函数声明采用了函数返回类型
后置的方法,该方法主要用于函数模板的返回值推导*/
auto sum(int a1, int a2)->int //返回后置类型,auto为返回值占位符
{
return a1 + a2;
}
返回类型推导 (C++14)
C++14标准支持对返回类型声明为auto的推导,例如:
/*下面代码,编译器会推导出auto为int。需要注意的是如果函数内有多个返回值,那么需要保证每次的
返回值类型都是相同的,否则会编译不过*/
auto sum(int a1, int a2) {return a1 + a2;}
lambada表达式形参 (C++14)
在C++14中,我们还可以把auto写到lambda表达式的形参列表中,这样就得到了一个泛型的lambda表达式,例如:
/*下面代码中a1被推导为int类型,a2被推导为double类型,返回值reval被推导为double类型*/
auto 1 = [](auto a1, auto a2) {return a1 + a2;};
auto reval = 1(5, 5.0);
/*下面是lambda表达式返回auto引用的方法。起初在后置返回类型中使用auto是不允许的,但是后
来人们发现,这是唯一让lambda表达式通过推导返回引用类型的方法了*/
auto 1 = [](int &i)->auto& {return i;}
auto x1 = 5;
auto &x2 = 1(x1);
assert(&x1 == &x2); //有相同的内存地址
声明成员变量时的初始化 (C++11 ~ C++17)
虽然C++11已经支持在声明成员变量时初始化,但是auto却无法在这种情况下声明非静态成员变量;另外静态成员变量是可以用auto声明并且初始化的,不过前提是auto必须使用const限定符,遗憾的是const限定符会导致i常量化,显然这不是我们想要的结果。
struct sometype {
auto i = 5; //错误,无法编译通过
};
struct sometype {
static const auto i = 5;
};
幸运的是,在C++17标准中,对静态成员变量,auto可以在没有const的情况下使用,例如:
struct sometype {
static inline auto i = 5; C++17
};
列表初始化对象的类型推导的新规则(C++17)
当auto关键字与列表初始化组合时,这里的规则有新老二个版本,这里只介绍C++17引入的新规则:
a. 直接使用列表初始化,列表中必须为单元素,否则无法编译,auto类型被推导为单元素的类型。
b.用等号加列表初始化,列表中可以包含单个或者多个元素,auto类型被推导为std::initializer_list
auto x1{1, 2}; //编译失败,不是单元素
auto x2{1}; //x2类型为int
auto x3 = {1, 2}; //x3类型为std::initializer_list
auto x4 = {1}; //x4类型为std::initializer_list
auto x5 {1, 2.0}; //编译失败,花括号中的元素类型不同
非类型模板形参占位符 (C++17)
C++17中,对auto关键字又一次进行了扩展,使它可以作为非类型模板形参的占位符。当然,我们必须保证推导出来的类型是可以用作模板形参的,否则无法通过编译。
#include
templete
void f()
{
std::cout << N << std::endl;
}
int main()
{
f<5>(); //N为int类型
f<'c'>(); //N为char类型
f<5.0>(); //编译失败,模板参数不能为double类型
}
函数形参列表 (C++20)
按照C++20之前的标准,无法在函数形参列表中使用auto声明形参(注意在C++14中,auto可以为lambda表达式声明形参)
void fun(atuo str) {...} //C++20之前编译失败,C++20编译成功