244

我一直在使用autoC++11 标准中提供的 new 关键字来处理复杂的模板类型,我相信它就是为此而设计的。但我也将它用于以下方面:

auto foo = std::make_shared<Foo>();

更怀疑的是:

auto foo = bla(); // where bla() return a shared_ptr<Foo>

我没有看到太多关于这个话题的讨论。似乎这auto可能会被过度使用,因为类型通常是文档和完整性检查的一种形式。您在哪里划定使用界限auto以及此新功能的推荐用例是什么?

澄清一下:我不是在寻求哲学观点;我要求标准委员会对该关键字的预期用途,可能会评论如何在实践中实现该预期用途。

4

15 回答 15

148

我认为auto当乍一看很难说如何编写类型时,应该使用关键字,但是表达式右侧的类型是显而易见的。例如,使用:

my_multi_type::nth_index<2>::type::key_type::composite_key_type::
    key_extractor_tuple::tail_type::head_type::result_type

获取复合键类型boost::multi_index,即使您知道它是int。你不能只写int,因为将来可能会改变。我会auto在这种情况下写。

因此,如果auto关键字在特定情况下提高了可读性,请使用它。auto当读者很清楚什么类型auto代表什么时,您可以编写。

这里有些例子:

auto foo = std::make_shared<Foo>();   // obvious
auto foo = bla();                     // unclear. don't know which type `foo` has

const size_t max_size = 100;
for ( auto x = max_size; x > 0; --x ) // unclear. could lead to the errors
                                      // since max_size is unsigned

std::vector<some_class> v;
for ( auto it = v.begin(); it != v.end(); ++it )
                                      // ok, since I know that `it` has an iterator type
                                      // (don't really care which one in this context)
于 2011-06-22T04:51:39.257 回答
64

尽可能在任何地方使用auto——尤其是const auto为了减少副作用。除了明显的情况外,您不必担心类型,但它们仍会为您静态验证,并且您可以避免一些重复。在auto不可行的情况下,您可以使用基于表达式的合同decltype在语义上表达类型。您的代码看起来会有所不同,但这将是一个积极的变化。

于 2011-06-22T05:41:47.980 回答
56

简单的。当您不在乎类型是什么时使用它。例如

for (const auto & i : some_container) {
   ...

我在这里关心i的是容器中的任何内容。

这有点像 typedef。

typedef float Height;
typedef double Weight;
//....
Height h;
Weight w;

在这里,我不在乎handw是浮点数还是双精度数,只关心它们是适合表达 heights 和 weights 的任何类型

或者考虑

for (auto i = some_container .begin (); ...

这里我关心的是它是一个合适的迭代器,支持operator++(),在这方面有点像鸭子打字。

此外,无法拼写 lambdas 的类型,所以auto f = []...是好的风格。另一种方法是强制转换,std::function但这会带来开销。

我真的无法想象auto. 我能想象的最接近的是剥夺自己对某种重要类型的显式转换——但你不会使用auto它,你会构造一个所需类型的对象。

如果您可以在不引入副作用的情况下删除代码中的一些冗余,那么这样做一定很好。

反例(借用别人的答案):

auto i = SomeClass();
for (auto x = make_unsigned (y); ...)

在这里我们确实关心类型是什么,所以我们应该写Someclass i;for(unsigned x = y;...

于 2011-09-19T14:31:36.573 回答
46

去吧。auto在任何可以使编写代码更容易的地方使用。

任何语言的每一个新特性都会被至少某些类型的程序员过度使用。只有通过一些有经验的程序员(不是菜鸟)的适度过度使用,其他有经验的程序员才能了解正确使用的界限。极端过度使用通常是不好的,但可能是好的,因为这种过度使用可能会导致功能的改进或更好的功能来替换它。

但是,如果我正在处理超过几行的代码,例如

auto foo = bla();

在类型被指示为零次的情况下,我可能想更改这些行以包含类型。第一个例子很好,因为类型被声明一次,并且auto使我们不必编写两次杂乱的模板类型。C++++ 万岁。但是明确显示类型零次,如果它在附近的行中不容易看到,让我感到紧张,至少在 C++ 及其直接继承者中是这样。对于其他设计为具有更多抽象、多态性和通用性的更高级别的语言,这很好。

于 2011-06-22T04:54:13.727 回答
43

C++ 和 Beyond 2012Ask Us Anything小组中,Andrei Alexandrescu、Scott Meyers 和 Herb Sutter 就何时使用和不使用auto. 跳到 25:03 分钟,进行 4 分钟的讨论。所有三个扬声器都给出了在何时不使用时应牢记的要点auto

我强烈鼓励人们得出自己的结论,但我的收获是在任何地方都使用,除非auto

  1. 它损害了可读性
  2. 存在关于自动类型转换的问题(例如,来自构造函数、赋值、模板中间类型、整数宽度之间的隐式转换)

自由使用explicit有助于减少对后者的关注,这有助于最大限度地减少前者成为问题的时间。

改写 Herb 所说的:“如果您不使用 X、Y 和 Z,请使用auto. 了解 X、Y 和 Z 是什么,然后auto在其他任何地方使用。”

于 2013-03-31T04:59:31.037 回答
39

是的,它可能被过度使用而损害可读性。我建议在确切类型很长、无法表达、或对可读性不重要且变量寿命很短的情况下使用它。例如,迭代器类型通常很长并且不重要,因此auto可以:

   for(auto i = container.begin(); i != container.end(); ++i);

auto这里不会损害可读性。

另一个例子是解析器规则类型,它可能很长而且很复杂。比较:

   auto spaces = space & space & space;

r_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&> spaces = 
   space & space & space;

另一方面,当类型已知且简单时,如果明确说明会更好:

int i = foo();

而不是

auto i = foo();
于 2011-06-22T04:56:36.787 回答
23

auto与线性代数库(如 Eigen 或 OpenCV)大量使用的表达式模板结合使用可能非常危险。

auto A = Matrix(...);
auto B = Matrix(...);
auto C = A * B; // C is not a matrix. It is a matrix EXPRESSION.
cout << C; // The expression is evaluated and gives the expected result.
... // <code modifying A or B>
cout << C; // The expression is evaluated AGAIN and gives a DIFFERENT result.

由此类错误引起的错误是调试的主要痛苦。一种可能的补救方法是,如果您一心想要将 auto 用于从左到右的声明样式,则将结果显式转换为预期的类型。

auto C = Matrix(A * B); // The expression is now evaluated immediately.
于 2015-02-20T20:50:58.280 回答
9

我使用auto没有限制,没有遇到任何问题。我什至有时最终将它用于简单的类型,例如int. 这使得 c++ 对我来说是一种更高级别的语言,并允许像在 python 中一样在 c++ 中声明变量。写完python代码后,我什至有时会写eg

auto i = MyClass();

代替

MyClass i;

这是我会说这是滥用auto关键字的一种情况。

通常我不介意对象的确切类型是什么,我对它的功能更感兴趣,并且由于函数名称通常会说明它们返回的对象,auto这并没有什么坏处:在 eg 中auto s = mycollection.size(),我猜这s将是一种整数,在我关心确切类型的极少数情况下,让我们检查函数原型(我的意思是,我更愿意检查何时需要信息,而不是在编写代码时先验,只是以防有一天它会有用,如int_type s = mycollection.size())。

关于接受的答案中的这个例子:

for ( auto x = max_size; x > 0; --x )

在我的代码中,我仍然auto在这种情况下使用,如果我想x不签名,那么我使用一个名为 say 的实用函数,make_unsigned它清楚地表达了我的担忧:

for ( auto x = make_unsigned(max_size); x > 0; --x )

免责声明:我只是描述我的使用,我没有能力提供建议!

于 2011-06-22T15:09:26.777 回答
2

我注意到的一个危险是在参考方面。例如

MyBigObject& ref_to_big_object= big_object;
auto another_ref = ref_to_big_object; // ?

问题是 another_ref 在这种情况下实际上不是引用,它是 MyBigObject 而不是 MyBigObject&。您最终会在没有意识到的情况下复制一个大对象。

如果您直接从方法中获得引用,您可能不会考虑它实际上是什么。

auto another_ref = function_returning_ref_to_big_object();

你需要“auto&”或“const auto&”

MyBigObject& ref_to_big_object= big_object;
auto& another_ref = ref_to_big_object;
const auto& yet_another_ref = function_returning_ref_to_big_object();
于 2014-02-21T06:04:28.607 回答
2

C++ 程序的主要问题之一是它允许您使用未初始化的变量。这导致我们讨厌的非确定性程序行为。应该注意的是,如果程序厌倦了使用它,现代编译器现在会抛出适当的/消息警告消息。

只是为了说明这一点,考虑下面的 c++ 程序:

int main() {
    int x;
    int y = 0;
    y += x;
}

如果我使用现代编译器(GCC)编译这个程序,它会给出警告。如果我们使用真正复杂的生产代码,这样的警告可能不是很明显。

main.cpp:在函数“int main()”中:

main.cpp:4:8:警告:在此函数中未初始化使用“x”[-Wuninitialized]

y += x;

    ^

==================================================== =============================== 现在如果我们更改使用 auto的程序,然后编译我们会得到以下结果:

int main() {
    auto x;
    auto y = 0;
    y += x;
}

main.cpp:在函数“int main()”中:

main.cpp:2:10:错误:'auto x' 的声明没有初始化器

 auto x;

      ^

使用自动,无法使用未初始化的变量。如果我们开始使用auto,这是我们可能(免费)获得的主要优势。

C++ 专家Herb Shutter在他的CppCon14演讲中解释了这个概念和其他伟大的现代 C++ 概念:

回到基础!现代 C++ 风格的精髓

于 2014-10-02T21:39:16.597 回答
1

auto在对推断类型有意义的地方使用。如果你有一个你知道是整数的东西,或者你知道它是一个字符串,只需使用 int / std::string 等。我不会担心“过度使用”一种语言特性,除非它达到荒谬的地步,或混淆代码。

无论如何,这是我的意见。

于 2011-06-22T04:52:36.103 回答
1

TL;DR:见底部的经验法则。

接受的答案表明以下经验法则:

auto乍一看很难说如何写类型,但表达式右侧的类型很明显时使用。

但我会说这太严格了。有时我不关心类型,因为该语句提供了足够的信息,而我无需花时间弄清楚类型。我的意思是什么?考虑一些答案中出现的示例:

auto x = f();

是什么让这成为滥用的一个例子auto?是我对f()返回类型的无知吗?好吧,如果我确实知道它可能确实会有所帮助,但是 - 这不是我主要关心的问题。更大的问题是,x而且f()毫无意义。如果我们有:

auto nugget = mine_gold();

相反,那么我通常不在乎函数的返回类型是否明显。阅读该声明,我知道我在做什么,并且我对返回值的语义有足够的了解,以至于我不需要知道它的类型。

所以我的回答是:auto只要编译器允许就使用它,除非:

  • 你觉得变量名和初始化/赋值表达式没有提供足够的关于语句正在做什么的信息。
  • 你会觉得变量名和初始化/赋值表达式一起提供了关于类型应该是什么的“误导性”信息——即,如果你必须猜测出现的是什么而不是自动你将能够做出猜测——它会是错了,这个错误的假设会在代码后面产生影响。
  • 您想强制使用不同的类型(例如引用)。

并且:

  • auto在替换为具体类型之前,最好给出一个有意义的名称(当然不包含类型名称) 。
于 2017-03-27T20:41:02.647 回答
0

auto关键字只能用于局部变量,不能用于参数或类/结构成员。因此,在您喜欢的任何地方使用它们是安全且可行的。我确实经常使用它们。类型是在编译时推断出来的,调试器在调试时显示类型,sizeof正确报告它,decltype会给出正确的类型 - 没有害处。我永远不会auto被过度使用!

于 2011-06-22T16:12:38.143 回答
0

有什么auto作用?

它告诉编译器根据变量的初始化值推断(确定)变量的数据类型。它使用类型推导。

应该auto用在什么地方?

  • 当您对了解变量的类型不感兴趣并且只想使用它时。

  • 当您想避免令人难以置信的长而丑陋的类型名时。

  • 当你不确定自己的类型时。

  • 当您不想在代码中看到未初始化的变量时,即自动强制您初始化变量,因此您不能忘记这样做。

什么时候不应该使用或缺点auto

  • 参考其功能, auto 可能会错误地推断类型,其中一种情况是
 std::vector<bool> vec(10, 0);

 auto x = vec[2];

 bool y = vec[2];

 std::cout << typeid(x).name() << "\n";

 std::cout << typeid(y).name() << "\n";

G++ 10.2 的输出令人惊讶:

St14_Bit_reference

b
  • 如果您想让您的代码对其他人可读和可理解,则不应使用它。它对读者隐藏了数据类型的可见性。
于 2021-07-28T14:26:43.220 回答
-6

我的痛苦经历之一auto将它与 lambda 表达式一起使用:

auto i = []() { return 0; };
cout<<"i = "<<i<<endl; // output: 1 !!!

实际上,这里i被解析为函数指针int(*)()。这只是一个简单的cout,但想象一下它在使用时会导致什么样的错误编译/运行时错误template

您应该避免 auto使用此类表达式并放置适当的return类型(或受控decltype()

上述示例的正确用法是,

auto i = []() { return 0; }(); // and now i contains the result of calling the lambda  
于 2011-06-22T04:57:13.227 回答