C++11 之 auto & decltype

关于 c++11 新特性,最先提到的肯定是类型推导,c++11 引入了 auto 和 decltype 关键字,使用他们可以在编译期就推导出变量或者表达式的类型,方便开发者编码也简化了代码。

auto

auto:让编译器在编译期间就推导出变量的类型,可以通过 = 右边的类型推导出变量的类型
auto 关键字基本的使用语法如下:auto name = value;
示例:

auto a = 10;  // 10 是 int 型,可以自动推导出 a 是 int

int i = 10;
auto b = i;  // b 是 int 型

auto d = 2.0;  // d 是 double 型

auto url = "http://c.biancheng.net/cplus/";  // 由双引号包围起来的字符串是 const char* 类型,所以推导出变量 url 的类型是 const char*,也即一个常量指针。

auto 的限制

  • auto 的使用必须马上初始化,否则无法推导出类型;
  • auto 在一行定义多个变量时,各个变量的推导不能产生二义性,否则编译失败;
  • auto 不能用作函数参数;
  • 在类中 auto 不能用作非静态成员变量;
  • auto 不能定义数组,可以定义指针;
  • auto 无法推导出模板参数。
    示例:
int i = 10;
auto a = i, &b = i, *c = &i;  // a是int,b是i的引用,c是i的指针,auto就相当于int

auto d = 0, f = 1.0;  // error,0和1.0类型不同,对于编译器有二义性,没法推导

auto e;  // error,使用auto必须马上初始化,否则无法推导类型

void func(auto value) {}  // error,auto不能用作函数参数

class A {
    auto a = 1;         // error,在类中 auto 不能用作非静态成员变量
    static auto b = 1;  // error,这里与 auto 无关,正常 static int b = 1 也不可以,因为静态成员变量不能在类中初始化
    static const auto int c = 1;  // ok
};

void func2() {
    int a[10] = {0};
    auto b = a;      // ok
    auto c[10] = a;  // error,auto不能定义数组,可以定义指针
    vector<int> d;
    vector<auto> f = d;  // error,auto无法推导出模板参数
}

对引用的处理
当表达式的类型为引用时, auto 会抛弃引用类型,直接推导出它的原始类型。

int n = 10;
int& r1 = n;

// auto推导
auto r2 = r1;
r2 = 20;
cout << n << ", " << r1 << ", " << r2 << endl;
// 打印结果:10, 10, 20

推导规则

首先,介绍下,这里的cv是指 cv 限定符,是 const 和 volatile 关键字的统称:

  • const 关键字用来表示数据是只读的,也就是不能被修改;
  • volatile 和 const 是相反的,它用来表示数据是可变的、易变的,目的是不让 CPU 将数据缓存到寄存器,而是从原始的内存中读取。

接着,看一下 auto 关键字对 cv 限定符的推导规则

  • 在不声明为引用或指针时,auto会忽略等号右边的引用类型和 cv 限定
  • 在声明为引用或者指针时,auto会保留等号右边的引用和 cv 属性
int i = 0;
auto* a = &i;  // a是int*
auto& b = i;   // b是int&
auto c = b;    // c是int,忽略了引用

const auto d = i;  // d是const int
auto e = d;        // e是int

const auto& f = e;  // f是const int&
auto& g = f;        // g是const int&

什么时候使用 auto?
这里没有绝对答案,个人认为:在不影响代码可读性的前提下尽可能使用 auto 是合适的,复杂类型就使用 auto,但对于像 int、double 这种基础数据类型就没有必要使用 auto,看下面这段代码:

auto func = [&] {
    cout << "xxx";
};  // 对于 func 你难道不使用 auto 吗,反正我是不关心 lambda 表达式究竟是什么类型。

auto asyncfunc = std::async(std::launch::async, func);
// 对于 asyncfunc 你难道不使用 auto 吗,我是懒得写 std::futurexxx 等代码,而且我也记不住它返回的究竟是什么...

decltypes

上面介绍 auto 用于推导变量类型,而 decltype 则用于推导表达式类型,这里只用于编译器分析表达式的类型,表达式实际不会进行运算,decltype 也是编译时期进行自动类型推导。
decltype 关键字基本的使用语法如下:decltype(exp) varname = value;

  • exp 是表达式,decltype(exp) 和exp 类型相同
  • exp 是函数调用,decltype(exp) 和函数返回值类型相同
  • 其它情况,若 exp 是左值,decltype(exp) 是 exp 类型的左值引用

示例:

int func() { return 0; }
decltype(func()) i;  // i为int类型

int x = 0;
decltype(x) y;      // y是int类型
decltype(x + y) z;  // z是int类型

int a = 0, b = 0;
decltype(a + b) c = 0;   // c是int,因为(a+b)返回一个右值
decltype(a += b) d = c;  // d是int&,因为(a+=b)返回一个左值

【注意1】decltype 不会像 auto 一样忽略引用和 cv 属性,decltype 会保留表达式的引用和 cv 属性:

cont int &i = 1;
decltype(i) b = 2; // b是const int&

【注意2】当表达式的类型为引用时,auto 和 decltype 的推导规则也不一样;decltype 会保留引用类型,而 auto 会抛弃引用类型,直接推导出它的原始类型。请看下面的例子:

int n = 10;
int& r1 = n;

// auto推导
auto r2 = r1;
r2 = 20;
cout << n << ", " << r1 << ", " << r2 << endl;

// decltype推导
decltype(r1) r3 = n;
r3 = 99;
cout << n << ", " << r1 << ", " << r3 << endl;
// 打印结果
// 10, 10, 20
// 99, 99, 99

【注意3】原则上讲,exp 就是一个普通的表达式,它可以是任意复杂的形式,但是我们必须要保证 exp 的结果是有类型的,不能是 void;例如,当 exp 调用一个返回值类型为 void 的函数时,exp 的结果也是 void 类型,此时就会导致编译错误。

只能使用 decltype 场景
auto 只能用于类的静态成员,不能用于类的非静态成员(普通成员),如果我们想推导非静态成员的类型,这个时候就必须使用 decltype 了。

#include <vector>
using namespace std;

template <typename T>
class Base {
public:
    void func(T& container) { m_it = container.begin(); }

private:
    decltype(T().begin()) m_it;  //注意这里
};

int main() {
    const vector<int> v;
    Base<const vector<int>> obj;
    obj.func(v);
    return 0;
}

auto 和 decltype 的配合—返回值类型后置

auto 和 decltype 一般配合使用在推导函数返回值的类型问题上。
下面这段代码:

template <typename T, typename U>
return_value add(T t, U u) {  // t和v类型不确定,无法推导出return_value类型
    return t + u;
}

上面代码由于 t 和 u 类型不确定,那如何推导出返回值类型呢,我们可能会想到这种

template <typename T, typename U>
decltype(t + u) add(T t, U u) {  // t和u尚未定义
    return t + u;
}

这段代码在 c++11 上是编译不过的,因为在 decltype(t +u) 推导时,t 和 u 尚未定义,就会编译出错,所以有了下面的叫做返回类型后置的配合使用方法:

template <typename T, typename U>
auto add(T t, U u) -> decltype(t + u) {
    return t + u;
}

返回值后置类型语法通过 auto 和 decltype 结合起来使用,目的为了解决函数返回值类型依赖于参数但却难以确定返回值类型的问题。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值