【问题标题】:Is `auto` specifier slower in compilation time?`auto` 说明符的编译时间是否较慢?
【发布时间】:2015-12-07 18:08:46
【问题描述】:

从 C++11 开始,我们可以使用 auto a = 1+2 代替 int a = 1+2 并且编译器会自行推断 a 的类型。它是如何工作的?在编译时(更多操作)是否比自己声明类型慢?

【问题讨论】:

  • 您是否尝试过使用和不使用auto 测量代码?相关:herbsutter.com/2013/08/12/…
  • 这是一个编译类型说明符,不影响运行时间。
  • 我的意思是更慢的编译时间
  • 老实说,我怀疑它的数量会很大,因为编译器无论如何都必须推导出“理想”表达式类型以确保它与 声明的类型兼容.所以我想实现 auto 可能建立在编译器已经在做的很多事情的基础上。
  • auto 仅在可以从表达式中推断出类型的情况下才合法,并且编译器必须确定表达式的类型以验证分配(非auto)是否有效。 另一方面,使用auto 意味着不会发生隐式转换,编译器也不需要验证是否发生缩小转换等。因此,auto 可能会编译得更快。在某些奇怪的情况下(在其他情况下由于程序员选择类型而进行转换),它甚至可能在运行时更快。

标签: c++ c++11 types auto compile-time


【解决方案1】:

它是如何工作的:

来自ISO/IEC

...auto 说明符是要推导的类型的占位符 (7.1.6.4)。其他简单类型说明符指定 先前声明的用户定义类型或基本类型之一...

7.1.6.4 自动说明符

  1. auto 类型说明符表示所声明的变量的类型应从其初始值设定项推导出来 或者函数声明器应包含尾随返回类型。
  2. auto 类型说明符可能与带有尾随返回类型的函数声明符一起出现在任何 此类声明符有效的上下文。
  3. 否则,变量的类型是从它的初始化器推导出来的。被声明的变量的名称 不应出现在初始化表达式中。在声明变量时允许使用这种 auto 块,在命名空间范围内,在 for-init-statement 中; auto 应作为 decl-specifiers 之一出现在 decl-specifier-seq 中,并且 decl-specifier-seq 后面应跟随一个或多个 initdeclarators,每个 initdeclarators 应具有非空初始化器...

例子:

auto x = 5; // OK: x has type int
const auto *v = &x, u = 6; // OK: v has type const int*, u has type const int
static auto y = 0.0; // OK: y has type double
auto int r; // error: auto is not a storage-class-specifier

是否更快:

简单的答案是是的,使用它可以省略很多类型转换,但是如果使用不当,它可能会成为很大的错误来源。

在 Bjarne Stroustrup 的一次采访中,他说auto 关键字为编码人员和编译器实现者带来了双赢局面。

【讨论】:

  • 对此我感到非常惊讶。我想象 auto 会花费更多的编译时间,但对我来说会更容易编写。如果你说的是真的,为什么不一直使用 auto 呢?它不会允许未初始化的变量 -> 出错的空间更小,并且更容易编写和更快
  • @CIsForCookies 好吧,可能不是一直如此,但绝对是在所有允许的情况下。在我看来,这是一个很棒的语言特性,应该将它纳入他的“字典”中。
【解决方案2】:

auto 要求 C++11 编译器 制作一些 limited 类型的 type inference(如果你想要更性感的类型,请查看 Ocaml推理语言)。但是开销只是编译时的。

如果您将auto a=1+2; 替换为int a=1+2;(两者具有相同的含义,请参阅answer by simplicis)并且如果您要求编译器使用optimize(甚至可能不要求优化),您可能会得到同样machine code。另见this

如果使用GCC,请尝试使用g++ -Wall -fverbose-asm -O -S foo.cc 编译一个小的C++11 foo.cc 文件,并查看(使用编辑器)生成的foo.s 汇编程序文件。您将看到生成的代码没有任何区别(但汇编文件可能会略有变化,例如由于调试信息等元数据)

如果您担心编译时间变慢,我猜使用auto 不是决定性因素(可能重载在编译时间上的成本可能更高)。 C++11 几乎被设计为实际上需要很多 optimizations(特别是复杂的内联和常量折叠和死代码消除),以及它的“解析”(特别是标题包含和模板扩展)是昂贵的。

Precompiling headers 和使用make -j(可能还有ccachedistcc)的并行构建可能有助于改善整体编译时间,远远超过避免auto

如果您想系统地避免auto(尤其是在range-for 循环中,例如std::map<std::string,int> dict; for (auto it: dict) {...}),您最终会输入更多的源代码(其解析和检查需要大量时间) 有更多的错误风险。正如here 解释的那样,您可能会稍微猜错类型,并且显式(稍微错误)可能会因为额外的转换而减慢代码的执行速度。

如果使用GCC,您可以将-ftime-report 传递给g++ 并获得有关各种GCC 通道和阶段的时间测量结果。

【讨论】:

  • auto 与编译器优化有什么关系? auto的含义由语言规定,auto a = 1 + 2;的含义与int a = 1 + 2;完全相同。
  • 确实如此,那又怎样? C++ 编译器实际上需要优化很多。
  • 是的。但这似乎与这个问题的主题无关。
  • 顺便说一句,占位符类型(和模板/值)正在使用概念进行完善,所以我不会参考 OCaml 进行性感类型推断,而是参考 N4377
  • 好吧,我还是觉得 Ocaml 的类型推断更性感。这是一个品味问题。
【解决方案3】:

编译器知道表达式(如1 + 2)计算的类型。这就是语言的工作方式——两个操作数的类型都是int,所以结果也是int。使用auto a,您只是告诉编译器“使用初始化表达式的类型”。

编译器不必在这里做任何额外的工作或推导。 auto 关键字只是让 you 从找出表达式和编写正确的类型中解脱出来。 (您可能会弄错,可能会产生意想不到的副作用 - 请参阅 this question(和 top answer),了解 auto 如何避免意外的运行时转换和复制的示例。


auto 关键字在迭代器中真正发挥作用:

std::vector< std::string >::const_iterator it = foo.cbegin();

auto it = foo.cbegin();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-06
    • 2013-07-04
    • 2014-12-10
    • 2023-01-04
    • 2021-07-11
    • 2017-09-28
    • 1970-01-01
    相关资源
    最近更新 更多