C ++ 11 auto关键字多less钱?

我一直在C ++ 11标准中使用新的auto关键字,用于复杂的模板types,这是我相信它的目的。 但我也使用它的东西,如:

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

更怀疑的是:

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

我没有看到有关这个话题的很多讨论。 看起来auto可能被过度使用,因为types通常是一种文件和健全性检查。 你在哪里画线使用auto和什么是这个新function的推荐用例?

澄清:我不是要求哲学观点, 我要求标准委员会对这个关键词进行预期的使用,可能会对实际中如何实现这个预期的使用做出评论。

附注:这个问题被转移到SE.Programmers,然后回到堆栈溢出。 关于这个的讨论可以在这个元问题中find。

我觉得应该使用auto关键字,但是很难说如何写一个types,但是expression式右边的types是显而易见的。 例如,使用:

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

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

所以如果auto关键字提高了在特定情况下的可读性,然后使用它。 当读者明显知道什么types的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) 

在任何地方都可以使用auto ,特别是const auto这样副作用就不那么重要了。 除了在明显的情况下,你不必担心types,但是它们仍然会被静态validation,你可以避免一些重复。 在auto不可行的情况下,可以使用decltype将语义types表示为基于expression式的合同 。 你的代码看起来不一样,但这将是一个积极的变化。

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

 for (const 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的对象。

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

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

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

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

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

任何语言的每个新function都会被至less某些types的程序员滥用。 只有通过一些有经验的程序员(而不是新手)适度的过度使用,其他有经验的程序员才能学习正确使用的界限。 极端的过度使用通常是不好的,但可能是好的,因为这种过度使用可能会导致function的改进或更好的替代function。

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

 auto foo = bla(); 

types被指示零次,我可能想要改变这些行以包括types。 第一个例子很好,因为这个types被声明了一次,并且auto避免了我们不得不两次编写混乱的模板types。 用于C ++++的Hooray。 但是明确地显示types为零的时间,如果在附近的行中不容易看到,那么至less在C ++及其后续版本中会让我感到紧张。 对于其他语言,devise更高层次更抽象,多态性和通用性,没关系。

是的,它可以被滥用,不利于可读性。 我build议在上下文中使用它,在这些上下文中,精确的types很长,或者是不可改变的,或者对于可读性来说不重要,variables是短暂的。 例如,迭代器types通常很长并且不重要,所以auto会起作用:

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

auto在这里不伤害可读性。

另一个例子是parsing器规则types,它可以是漫长而复杂的。 比较:

  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; 

另一方面,当types是已知的并且简单的时候,如果明确地陈述的话会好得多:

 int i = foo(); 

而不是

 auto i = foo(); 

在“ Ask Us Anything”面板的C ++和Beyond 2012中, Andrei Alexandrescu,Scott Meyers和Herb Sutter谈论了何时使用而不使用auto的奇妙交stream 。 跳到分钟25:03进行4分钟的讨论。 三位演讲嘉宾都给出了不应该使用auto时应该注意的一些要点。

我高度鼓励人们自己做出结论,但是我所拿走的东西就是要使用auto除非

  1. 它伤害可读性
  2. 有关于自动types转换(例如从构造函数,赋值,模板中间types,整数宽度之间的隐式转换)

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

改写Herb所说的话:“如果你不做X,Y和Z,那么使用auto ,学习X,Y和Z是什么,然后去其他地方使用auto

auto可能是非常危险的与expression式模板结合在一起被线性代数库如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. 

由这种types的错误引起的错误是debugging的一大痛苦。 一种可能的补救措施是将结果明确地转换为预期的types,如果你对从左到右的声明风格使用auto的话。

 auto C = Matrix(A * B); // The expression is now evaluated immediately. 

我用auto wihout限制,并没有面临任何问题。 我甚至有时最终将它用于像int这样的简单types。 这使得c ++成为更高层次的语言,并且允许像在Python中一样在c ++中声明variables。 在编写python代码之后,我甚至有时会编写例如

 auto i = MyClass(); 

代替

 MyClass i; 

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

通常我不介意什么是对象的确切types,我对它的function更感兴趣,而且函数名称通常会说明它们返回的对象, auto不会伤害它:例如auto s = mycollection.size() ,我可以猜测s是一种整数,而在我关心确切types的罕见情况下,让我们来检查函数原型(我的意思是,我更喜欢检查什么时候需要信息,而不是在编写代码时的先验,以防万一有一天它会被使用,例如int_type s = mycollection.size() )。

关于这个从被接受的答案中得到的例子:

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

在我的代码中,我仍然在这种情况下使用auto ,如果我想要x是无符号的,那么我使用了一个名为make_unsigned的实用函数,它清楚地expression了我的担忧:

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

免责声明:我只是描述我的使用,我没有资格提供build议!

我注意到的一个危险就是参考。 例如

 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(); 

你会需要“自动&”或“常量自动&”

 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(); 

C ++程序的一个主要问题是它允许你使用未初始化的variables 。 这导致我们讨厌非确定性的程序行为。 应该指出,现代编译器现在抛出适当的/消息的警告消息,如果程序轮胎使用它。

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

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

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

main.cpp:在函数'int main()'中:

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

y + = x;

  ^ 

================================================== ===============================现在,如果我们改变我们的程序使用自动 ,然后编译我们得到以下内容:

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

main.cpp:在函数'int main()'中:

main.cpp:2:10: 错误 :'auto x'的声明没有初始值设定项

  auto x; ^ 

使用auto时,不可能使用未初始化的variables。 这是我们可能获得(免费),如果我们开始使用汽车的主要优势。

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

回到基础! 现代C ++风格的要点

使用auto ,它是有道理的推断types。 如果你有一个你知道的是一个整数,或者你知道它是一个string,只要使用int / std :: string等。我不会担心语言function的“过度使用”,除非它达到了荒谬的地步,或混淆代码。

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

auto关键字只能用于局部variables,而不能用于参数或类/结构成员。 所以,在任何你喜欢的地方使用它们是安全和可行的。 我确实使用了很多。 types是在编译时推导出来的,debugging器显示debugging时的types, sizeof报告正确, decltype会给出正确的types – 没有任何伤害。 我不认为auto被滥用,永远!

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

接受的答案build议以下经验法则:

使用auto当很难说如何写一种types的一见钟情,但expression的右侧的types是显而易见的。

但我会说这太严格了。 有时候我不关心types,因为这个语句足够丰富,而且不费吹灰之力就可以抽出时间。 这是什么意思? 考虑一些答案中popup的例子:

 auto x = foo(); 

是什么让这个滥用auto的例子? 难道我不知道foo()的返回types是什么? 那么,如果我清楚地知道,那会更好,但这不是我主要关心的问题。 我会说, x是一个(几乎)无意义的variables名,而foo()的含义还不够清楚。 至less为了这个例子,让我们说这不是。 如果我们拥有完全相同types的东西,

 auto result = frobnicate_the_bar(); 

那么我通常不关心函数的返回types是否明显。 阅读这个陈述,我知道自己在做什么,而且我对于返回值的语义知之甚less,并不需要知道它的types。

所以我的答案是:编译器允许时使用auto ,除非:

  • 你觉得variables名和初始化/赋值expression式不能提供有关语句正在做什么的足够的信息。
  • 你觉得variables名和初始化/赋值expression式一起提供了关于types应该是什么types的“误导性”的信息 – 也就是说,如果你必须猜测什么是auto而不是auto,那么你就可以猜测 – 这将是错误的,这个错误的假设在代码后面有影响。
  • 你想强制一个不同的types(例如一个参考)。

并且:

  • 在将autoreplace为具体types之前,首先给出一个有意义的名称(当然不包含types名称)。

我对auto驾驶的一个苦恼就是用lambdaexpression式来使用它 :

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

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

你应该避免使用这样的expression式auto ,并把适当的returntypes(或控制的decltype()

以上例子的正确用法是,

 auto i = []() { return 0; }(); // and now i contains the result of calling the lambda