新的关键字“自动”; 什么时候应该用来声明一个variablestypes?

可能重复:
用C ++ 0x auto关键字多less钱

我们(作为一个社区)是否有足够的经验来确定汽车何时和/或是否被滥用?

我真正想要的是一个最佳实践指南

  • 何时使用自动
  • 何时应该避免

简单的经验法则可以在80%的案件中迅速得到遵守。

作为一个背景,这个问题是由我在这里的回应引发的

我认为当这个types在项目中工作(或者工作)的协程序员中非常有名,那么可以使用auto ,比如下面的代码:

 //good : auto increases readability here for(auto it = v.begin(); it != v.end(); ++it) //v is some [std] container { //.. } 

或者更一般地说,

 //good : auto increases readability here for(auto it = std::begin(v); it != std::end(v); ++it)//v could be array as well { //.. } 

但是当这个types不是很知名和不常用,那么我认为auto似乎降低了可读性,比如这里:

 //bad : auto decreases readability here auto obj = ProcessData(someVariables); 

而在前一种情况下, auto的使用看起来非常好,不会降低可读性,因此可以广泛使用,但在后一种情况下,它降低了可读性,因此不应使用。


另一个可以使用auto地方是当你使用new ,比如这里:

 //without auto. Not that good, looks cumbersome SomeType<OtherType>::SomeOtherType * pObject = new SomeType<OtherType>::SomeOtherType(); //With auto. good : auto increases readability here auto pObject = new SomeType<OtherType>::SomeOtherType(); 

这是非常好的,因为它减less了键盘的使用,而不会降低可读性,因为任何人只要通过查看代码就可以知道pObject的types。


有时候,这种types是如此的不相干,甚至不需要types的知识,比如在expression式模板中 ; 事实上, 实际上不可能写出正确的types,在这种情况下, auto对于程序员是一种解脱。 我写了expression式模板库,可以用作:

 foam::composition::expression<int> x; auto s = x * x; //square auto c = x * x * x; //cube for(int i = 0; i < 5 ; i++ ) std::cout << s(i) << ", " << c(i) << std::endl; 

输出:

 0, 0 1, 1 4, 8 9, 27 16, 64 

现在比较上面的代码和下面的不使用auto 等价代码:

 foam::composition::expression<int> x; //scroll horizontally to see the complete type!! foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply>> s = x * x; //square foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<foam::composition::details::binary_expression<foam::composition::expression<int>, foam::composition::expression<int>, foam::operators::multiply> >, foam::composition::expression<int>, foam::operators::multiply>> c = x * x * x; //cube for(int i = 0; i < 5 ; i++ ) std::cout << s(i) << ", " << c(i) << std::endl; 

正如你所看到的,在这种情况下, auto让你的生活更加容易。 上面使用的expression式非常简单, 考虑一些更复杂的expression式的types:

 auto a = x * x - 4 * x + 4; auto b = x * (x + 10) / ( x * x+ 12 ); auto c = (x ^ 4 + x ^ 3 + x ^ 2 + x + 100 ) / ( x ^ 2 + 10 ); 

这种expression式的types会更加巨大和丑陋,但是由于auto ,我们现在可以让编译器推断expression式的types。


所以底线是:关键字auto可能会增加或减less代码的清晰度和可读性, 具体取决于上下文 。 如果上下文清楚地说明了它是什么types ,或者至less应该如何使用(在标准容器迭代器的情况下),或者甚至不需要知道实际types(例如在expression式模板中),那么auto应该是使用 ,如果上下文没有说清楚,也不是很常见(比如上面第二种情况),那么最好避免

简单。 当你不关心types是什么时使用它。 例如

 for (auto i : some_container) { ... 

我所关心的只是i在容器里的任何东西。

这有点像typedefs。

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

在这里,我不在乎hw是浮动还是双打,只是它们是适合expression高度和重量的types

或考虑

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

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

此外,lambda的types不能拼写,所以auto f = []...是好风格。 另一种方法是转换为std::function但是会带来开销。

我不能真正想到auto的“滥用”。 我能想象到的最接近的做法是剥夺自己对某种重要types的显式转换 – 但是您不会使用auto来创build所需types的对象。

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

我会在C#中应用与var相同的规则:大量使用它 。 它增加了可读性。 除非variables的types实际上足够重要,否则不能明确说明,在这种情况下应该这样做(duh)。

不过,我认为(特别是在静态types语言中),编译器在跟踪types方面比我们好得多。 大多数情况下, 确切types不是非常重要的(否则界面在实践中将不起作用)。 了解哪些操作是允许的更重要。 上下文应该告诉我们。

此外, auto实际上可以防止错误 ,防止初始化中不需要的隐式转换。 一般来说, Foo x = y; 将执行隐式转换,如果y不是Footypes并存在隐式转换。 这是避免首先隐式转换的原因。 不幸的是,C ++已经太多了。

auto x = y; 原则上会防止这个问题。

另一方面,应该清楚的是,当我在执行计算时假定这个或者整数字节的数量,variables的显式types必须是已知的,并且应该清楚地说明。

并不是所有的案件都是明确的裁决,但是我认为大多数案件都是这样的

  1. 在大多数情况下,很容易看出明确的types是否需要知道
  2. 显式types的需求相对较less。

C#编译器团队的首席开发人员Eric Lippert 对于var说法大同小异 。

我想你的第一个问题的答案是不是。 我们足够了解一些关于什么时候使用或避免auto指导方针,但是他们仍然留下了很多我们现在可以说的最好的情况,就是我们还不能给予客观的build议。

当你想要(例如)适当的types来保存两个generics参数的某些操作的结果时,几乎不得不使用它的明显情况就是在模板中。 在这样的情况下,滥用的唯一可能性不会真的是滥用auto本身,而是你正在做的一般操作types(或者你正在写的模板的types等)是否是你想要的避免更好。

至less有一些情况下,你显然需要避免auto 。 如果您使用的是类似代理types的代理types,则需要根据从代理 – >目标转换来完成部分工作, auto会(尝试)创build与源代码types相同的目标,以便转换不会发生。 在某些情况下,这可能会延迟转换,但在其他情况下根本无法工作(例如,如果代理types不支持分配,通常情况下)。

另一个例子是,当你需要确保一个特定的variables为了类似于外部接口而具有特定的types时。 举个例子,考虑将networking掩码应用到IP(v4)地址。 为了说明起见,我们假设你正在使用地址的单个八位字节(例如,将每个unsigned char表示为一个unsigned char ),所以我们最终得到了octets[0] & mask[0] 。 由于C的types提升规则,即使两个操作数都是unsigned char ,结果通常是int 。 我们需要的结果是一个unsigned char尽pipe(即一个八位字节)不是一个int (通常是4个八位字节)。 因此,在这种情况下, auto几乎肯定是不合适的。

尽pipe如此,这仍然留下了很多案件。 我个人倾向于这种情况是把auto当作默认值,只在显式types的情况下,至less有点类似于上面我提到的后一种情况 – 即使不需要特定的types操作,我真的想要一个特定的types,即使这可能涉及隐式转换。

我的猜测(但这只是一个猜测)是,随着时间的推移,我可能更倾向于这个方向。 当我习惯了编译器挑选types时,我会发现有相当多的情况下,我现在认为我应该指定types,我真的不需要,代码将会很好。

我怀疑我们中的很多人(以及我们年纪较大/经验丰富,可能更糟糕的是我们会这样做)会使用显式types的原因,最终追溯到对性能的一些感觉,并相信我们的select会提高性能。 部分时间我们甚至可能是对的 – 但是正如我们大多数有这么多经验的人所发现的那样,我们的猜测常常是错误的(特别是当它们基于隐含的假设时),编译器和处理器通常在这些事情上变得更好随着时间的推移。

我已经使用完整types推理的语言。 我认为没有理由不把auto放在任何地方,这在技术上是可行的*。 其实我可能已经写了auto i = 0; ,其中int是一个比auto短的字符。 我甚至不知道我做了什么,因为底部是:我不关心明确的打字。

*:例如auto int[] = { 0, 1, 2, 3 }不起作用。

只能使用长重复types,如长模板和lambda函数types。 尽量避免它,如果你能弄清楚事情。