19

自动地推导变量数据类型,C++11标准中的 auto 关键字用法详解,如何使用auto定义模板...

 3 years ago
source link: https://blog.popkx.com/%E8%87%AA%E5%8A%A8%E5%9C%B0%E6%8E%A8%E5%AF%BC%E5%8F%98%E9%87%8F%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B-C-11%E6%A0%87%E5%87%86%E4%B8%AD%E7%9A%84-auto-%E5%85%B3%E9%94%AE%E5%AD%97%E7%94%A8%E6%B3%95%E8%AF%A6%E8%A7%A3-%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8auto%E5%AE%9A%E4%B9%89%E6%A8%A1%E6%9D%BF%E5%87%BD%E6%95%B0-%E4%BD%BF%E7%94%A8auto%E7%9A%84/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

随着计算机性能的持续提升,编程语言似乎迎来了一次大爆发,各种编程语言不断出现,乐意折腾的人总能找到一门适合自己胃口的编程语言。

程序员的口味大体可以分为两种:一是追求极致程序效率,一是追求极致开发效率。抛开稍显晦涩的汇编语言不谈,前者以C语言程序员为代表,C语言语法简单,可控性强,更贴近机器,适合开发超高效率的程序。后者则以各种偏脚本化的语言程序员为代表,这类编程语言更贴近人类,因此开发效率很高。

程序中变量的类型

Python程序中变量的类型

下面是一段 Python 代码,可以看出,Python 程序员在定义变量时,甚至无需关心变量的类型,只需写出核心逻辑即可:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
counter = 100 # 赋值整型变量
miles = 1000.0 # 浮点型
name = "John" # 字符串

不过,程序中变量的数据类型本质上是用于告诉计算机其占用内存的大小,以及该如何解释这段数据的。所以,即使是 Python 程序,在运行时也需要确定变量的类型,否则计算机就不知道如何存储和解释程序中的变量。当然了,由于上面这段代码没有显式的指定变量类型,所以确定数据类型的工作只能交给编译器(或者说解释器)了。

C++程序中变量的类型

不要小看 Python 的这个特性,这可以省去相当多的“键盘敲击次数”。作为实例,可以参考下面这段C++程序片段:

...
std::list<tracking_cars_info> cars_list;
...
std::list<tracking_cars_info>::iterator it;
for (it=cars_list.begin(); it!=cars_list.end(); it++) {
    ...
}

为了遍历 list 中的元素,上述C++代码定义了迭代器it,可以看出,这个“定义”过程需要敲击相当多的字符:“std::list<tracking_cars_info>::iterator ”。若是C++也允许像Python那样,无需显式的指定变量类型,而是把变量类型的确认工作交给编译器,代码就简洁许多了:

...
std::list<tracking_cars_info> cars_list;
...
for (it=cars_list.begin(); it!=cars_list.end(); it++) {
    ...
}

这样的C++代码并不影响阅读,熟悉 list 的程序员一眼就能看出it是一个迭代器。

可能有读者觉得将“确认变量类型”的工作交给编译器会降低编译效率,但是其实仔细想一下,应该并非如此,请看下面这段C++代码:

int a = 0;
int *p = &a;
a = p;

编译器在处理第三行代码时,大都会报错,给出类似于下面这样的信息:

error: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]

这说明C++编译器在处理变量赋值时,也需要确定右值的类型,因此从理论上来看,自动推导类型并不会带来多少额外的工作。

C++中的auto关键字

事实上,C++11标准的确增加了“变量类型自动推导”的功能,只不过不像 Python,C++的这一功能需要借助auto关键字。

其实早在C++98标准中,就已经存在auto关键字了,只不过那时的auto关键字仅用于声明变量拥有自动的生命周期,可是不使用 auto 关键字定义的变量仍然具有自动的生命周期,所以那时的 auto 关键字属于多余的特性。

C++11标准丢弃了auto关键字之前的特性,取而代之的,将此关键字用于定义“可自动推导类型”的变量。下面是一段C++代码示例,请看:

int a = 8;
auto b = a; // 自动推导 b 的类型为 int
cout << typeid(b).name() << endl;
自动推导类型的C++代码示例

自动推导类型的C++代码示例

编译这段C++代码时应注意指定C++11标准:

# g++ t.cpp -std=c++11
# ./a.out 
i

可见,程序输出了i,表示变量 b 为 int 类型,这说明auto关键字的确根据 b 的右值(int型的a)确定了 b 的类型为 int。

现在有了auto关键字,前面那个遍历 list 的C++代码片段就可以改写了,请看:

for (std::list<tracking_cars_info>::iterator it=cars_list.begin(); it!=cars_list.end(); it++){
    ...
}
// 现在可以改写为
for (auto it=cars_list.begin(); it!=cars_list.end(); it++){
    ...
}

显然简洁了许多,C++程序员也可以少敲很多次键盘,手指健康得到了一定程度的保护。

C++的 auto 关键字用于模板

上面只是C++中 auto 关键字的其中一种用法,事实上,auto 关键字不仅仅能够让程序员少敲代码,也能带来一些功能上的便利。例如,在定义模板函数时,用于声明依赖模板参数的变量类型,下面是一段C++代码示例:

template <typename _Tx,typename _Ty>
void add(_Tx x, _Ty y)
{
    auto v = x+y;
    std::cout << v;
}

请注意变量 v 的定义,如果这里不使用 auto 关键字就棘手多了,因为我们根本无法事先确定变量 x 和 y 的类型,不到编译的时候,谁能知道 x+y 的结果究竟是什么类型呢?

读者应该注意到上面的 add() 函数其实并不好用,因为它没有将结果返回给调用者。于是可以做如下修改,请看相关C++代码:

template <typename _Tx, typename _Ty>
auto add(_Tx x, _Ty y)
{
    return x+y;
}

可见,auto 关键字也可用于自动推导模板函数的返回值类型,否则 add() 函数的返回值类型也是相当难确定的。不过,在编译这段C++代码时,发现如下警告信息:

warning: ‘add’ function uses ‘auto’ type specifier without trailing return type [enabled by default]
 auto add(_Tx x, _Ty y)

要避免出现这样的警告信息,可以使用C++11标准引入的新关键字decltype,相关的C++代码如下,请看:

template <typename _Tx, typename _Ty>
auto add(_Tx x, _Ty y)->decltype(x+y)
{
    return x+y;
}
auto用于定义模板函数

auto用于定义模板函数

auto 在这里的作用也称为返回值占位,它只是为函数返回值占了一个位置,真正的返回值是后面的decltype(x+y)。现在编译这段C++代码,可以得到如下输出,请看:

# g++ t.cpp -std=c++11
# ./a.out 
9.14
7
145

auto 关键字的“自动推导类型”功能是由编译器提供的,而编译器也不是占卜得到变量类型的,它需要知道一些信息,因此 auto 在定义变量时必须初始化:

int i = 3;
auto j = i;

只有这样,编译器才能根据右值的类型推导出 auto 变量的类型。也正是因为如此,auto 变量作为函数的参数是非法的:

// 非法
void foo(auto a){
    ...
}

若是使用 auto 关键字在同一行定义多个变量,这些变量必须是同一类型,否则编译器就会报错,例如下面这两行C++代码:

auto a1 = 1, a2 = 2;//正确
auto b1 = 10, b2 = 'a';//错误,没有推导为同一类型

如果 auto 变量初始化时的右值为引用,则去除引用。请看下面这段C++代码:

int a = 1;
int &b = a;

auto c = b; // 此时 c 的类型为 int

c = 100; // a 依然为 1

如果希望自动推导为引用类型,则需要配合&运算符:

auto &d = b;
d = 100; // 此时 a 为 100

类似的,如果初始化表达式带有 const 或者 volatile 修饰符,仅有 auto 定义的变量去除 const 和 volatile 修饰符。

最后要说明的是,C++中的 auto 关键字并不是真正的类型,它仅用于告诉编译器“应该自动推导变量的类型”,所以像 sizeof() 以及 typeid() 这样操作数据类型的操作符是不能用于 auto 的,下面这两行C++代码是非法的:

size = sizeof(auto); // 非法
cout << typeid(auto).name() << endl; // 非法

本节主要讨论了C++11标准中的 auto 关键字,可见,它不仅能够让程序员少敲键盘,提升了C++程序开发效率,还额外提供了一些功能上的便利。文章在最后还讨论了使用 auto 关键字的注意事项,希望对读者有所帮助。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK