尽可能使用 auto

自动类型推导是现代C++中最重要且使用最广泛的功能之一。 新的C++标准使得可以在各种上下文中将auto用作类型的占位符,并让编译器推断出实际的类型。 在C++ 11中,auto可以用于声明局部变量和具有尾随返回类型的函数的返回类型。 在C++ 14中,auto可以用于函数的返回类型而无需指定尾随类型,并且可以用于lambda表达式中的参数声明。 未来的标准版本可能会将auto的使用范围扩大到更多情况。 在这些情况下使用auto有几个重要的好处。 开发人员应该意识到它们,并尽可能选择auto。 实际术语是由Andrei Alexandrescu创造的,并由Herb Sutter推广,即“几乎总是自动”(AAA)。

怎么做………

在以下情况下,请考虑将auto用作实际类型的占位符:

  • 当您不想提交给特定类型时,以auto name = expression形式声明局部变量:
  • 当需要提交特定类型时,以 auto name = type-id {expression}形式声明局部变量:
  • 以 auto name = lambda-expression的形式声明命名的lambda函数,除非需要传递lambda或返回到函数:
  • 声明lambda参数并返回值:
  • 在不想提交特定类型时声明函数返回类型:

这个如何起作用……

auto 指示符基本上是实际类型的占位符。 使用auto时,编译器从以下实例推导实际类型:

  • 从用于初始化变量的表达式的类型开始,使用auto声明变量时。
  • 从尾随的返回类型或函数的返回表达式的类型开始,当auto用作函数的返回类型的占位符时。

在某些情况下,有必要提交给特定类型。 例如,在前面的示例中,编译器将s的类型推导为char const *。 如果要使用std::string,则必须明确指定类型。 类似地,v的类型推导为std::initializer_list 。 但是,其目的可能是拥有一个std::vector 。 在这种情况下,必须在赋值的右侧明确指定类型。

使用 auto 指示符而不是实际类型有一些重要的好处; 以下是一些最重要的几个:

  • 无法使变量保持未初始化状态。 这是开发人员在声明用于指定实际类型的变量时犯的一个常见错误,但是对于auto来说,编译器不可能通过初始化变量来推断类型。
  • 使用 auto 可确保您始终使用正确的类型,并且不会发生隐式转换。 考虑以下示例,在该示例中,我们检索向量的大小并传给局部变量。 在第一种情况下,变量的类型为int,尽管size()方法返回size_t。 这意味着将发生从size_t到int的隐式转换。 但是,对类型使用auto可以推断出正确的类型,即size_t:
  • 使用auto可以促进良好的面向对象的实践,例如,优先使用接口而不是具体实现。 指定的类型数量越少,代码就越通用,并且对将来的更改更开放,这是面向对象编程的基本原理。
  • 这意味着更少的输入,以及更少的关心我们不并不需要关心的实际类型。 即使我们明确指定了类型,但实际上我们并不在乎它。 迭代器是一种非常常见的情况,但是可以想到更多的情况。 当您要遍历一个范围时,您不必关心迭代器的实际类型。 您只对迭代器本身感兴趣; 因此,使用 auto 可以节省键入长名称的时间,并帮助您专注于实际代码而不是键入名称。 在以下示例中,在第一个for循环中,我们显式使用迭代器的类型。 要键入的文本很多,长语句实际上会使代码的可读性降低,并且您还需要知道实际上并不关心的类型名称。 auto 指示符的第二个循环看起来更简单,使您不必键入和关心实际类型。
  • 使用auto声明变量可提供一致的编码样式,其类型始终在右侧。 如果动态分配对象,则需要在赋值的左侧和右侧都写类型,例如,int p = new int(42)。 使用auto时,类型仅在右侧指定一次。

但是,使用auto时有一些陷阱:

  • auto 指示符仅是该类型的占位符,而不是const / volatile和引用说明符。 如果需要const / volatile和/或引用类型,则需要显式指定它们。 在下面的示例中,foo.get()返回对int的引用; 当从返回值初始化变量x时,编译器推导的类型为int,而不是int&。 因此,对x的任何更改都不会传播到foo.x_。 为此,应使用auto&:
  • 不能将auto用于不可移动的类型:
  • 对于多字类型(例如long long,long double或struct foo),不能使用auto。 但是,在第一种情况下,可能的解决方法是使用文字或类型别名。 与第二种情况一样,仅出于C兼容性在C ++中支持以这种形式使用struct / class,无论如何都应避免:
  • 如果使用 auto 说明符,但仍需要知道类型,则可以在任何IDE中通过将光标置于变量上来做到这一点。 但是,如果您退出IDE,那将是不可能的了,知道实际类型的唯一方法是自己从初始化表达式中推断出它,这可能意味着在代码中搜索函数返回类型。

auto可用于指定函数的返回类型。 在C ++ 11中,这需要在函数声明中添加尾随返回类型。 在C ++ 14中,这已经放宽了,并且返回值的类型由编译器从return表达式推导出。 如果有多个返回值,则它们应具有相同的类型:

如前所述,auto不保留const / volatile和引用限定符。 这导致auto作为函数的返回类型的占位符出现问题。 为了解释这一点,让我们考虑使用foo.get()的前面的示例。 这次,我们有一个名为proxy_get()的包装函数,该函数接受一个foo的引用,调用get(),并返回get()返回的值int&。 但是,编译器将推断proxy_get()的返回类型为int,而不是int&。 尝试将该值分配给int&失败,并显示以下错误:

要解决此问题,我们需要实际返回auto&。 但是,这是模板的问题,可以完美地转发返回类型,而无需知道这是值还是引用。 C ++ 14中此问题的解决方案是decltype(auto),它将正确推断出类型:

可以使用auto的最后一个重要情况是使用lambda。 从C ++ 14开始,lambda返回类型和lambda参数类型都可以是自动的。 这样的lambda称为通用lambda,因为由lambda定义的闭包类型具有模板化调用运算符。 下面显示了一个通用lambda,它带有两个自动参数,并返回对实际类型应用operator +的结果:

此lambda可用于添加为其定义了operator +的任何内容。 在下面的示例中,我们使用lambda来添加两个整数并将其连接到std::string对象(使用C ++ 14用户定义的文字operator “”s):

参见

  • 创建类型别名和别名模板
  • 了解统一初始化

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据