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


218

我一直在使用autoC ++ 11标准中的new 关键字来处理复杂的模板化类型,这是我认为它的设计目的。但是我还将它用于诸如以下的事情:

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

更怀疑的是:

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

关于这个主题,我还没有看到太多讨论。auto由于类型通常是文档和健全性检查的一种形式,因此似乎可以过度使用。您在哪里划界线auto?此新功能的建议用例是什么?

需要澄清的是:我不是在征求哲学意见;我要求标准委员会提供此关键字的预期用途,并可能对在实践中如何实现预期用途提出意见。

旁注:此问题移至SE.Programmers,然后返回到堆栈溢出。关于这个的讨论可以在这个元问题中找到。


但是,这是一个问答站点,而不是讨论站点。您提出了一个非常非常笼统的问题,我怀疑除了高度主观的问题之外,谁能给您其他任何东西。(这就是为什么-1)
TravisG 2011年

15
@heishe,我添加了一个说明。如果您非常笼统地阅读问题,那么它似乎确实是在征求主观意见,但是实际上,如果您使用了auto关键字,那么您知道应该如何使用它。这就是我要问的问题,作为对此功能的新手,我应该如何使用它?
艾伦·图灵

13
在C#引入时,到处都是这种讨论var(也就是说,一旦人们忘记了它毕竟不是动态类型的想法)。如果您愿意,可以从这个问题开始,然后解决相关问题。
R. Martinho Fernandes

2
@Lex:某事合法或不合法;根据定义,称合法的“坏”东西是主观的。即,称auto foo = bla();“不好”显然是一种观点,而不是事实,这使这个问题得以解答,并使其与程序员SE相关,这正是密切投票所表明的。/ shrug
ildjarn 2011年

3
赫伯·萨特(Herb Sutter)对此事的看法:Herbutter.com/2013/06/13/…– rafak 2013
6

Answers:


131

我认为,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)

18
这似乎不是一个很好的建议。您多久不知道对象的类型?(也就是说,在模板之外。)如果您不知道它们是否键入,请查找它,不要懒惰并使用auto。
Paul Manta

9
@Paul:通常您只知道或只需要知道类型的最重要部分,例如它是一个迭代器,但是您不知道也不关心它是向量的迭代器还是链接的迭代器清单 在这种情况下,您确实不想花费时间和屏幕空间来弄清楚如何写下类型。
Lie Ryan

45
不管C ++顽固主义者喜欢不喜欢,C ++ 0x都会吸引那些从未使用过C ++的人。那些将在所有地方使用自动。
Falken教授

6
@ R.MartinhoFernandes -不,这是明确的唯一的事情是,无论 bla()你给它的回报foo
Luis Machuca

8
@LuisMachuca我的回答是一种嘲讽的态度,说给教科书示例错误的变量和函数命名并将其缺乏可读性归咎于类型推断是不诚实的。
R. Martinho Fernandes

62

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


12
我特别要说'const auto&'
Viktor Sehr

2
更好地利用auto&&在复杂情况下edmundv.home.xs4all.nl/blog/2014/01/28/...
KindDragon

2
@KindDragon:这是一个很好的经验法则,但是我更喜欢使用const auto&const auto除非我明确想要变异或移动。
乔恩·普迪2014年

在所有可能的地方使用自动 ”。这是否意味着我应该写auto str = std::string();而不是std::string str;
卡尔马留斯

4
在各处使用auto会使代码的可读性和调试难度降低,因为在读取代码时必须自己推断类型。
SubMachine

52

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

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

我在这里关心的就是i容器中的所有内容。

这有点像typedef。

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

在这里,我不在乎hw是浮点数还是双精度数,只是它们是适合于表示身高和体重的任何类型

或考虑

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

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

同样,lambda的类型不能拼写,auto f = []...好的样式也是如此。另一种选择是强制转换,std::function但这会带来开销。

我无法真正想到的“滥用” auto。我能想到的最接近的方法是剥夺自己对某种重要类型的显式转换-但您不会用auto它,而是可以构造所需类型的对象。

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

反例(从别人的答案中借来的):

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

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


1
嗯 没那么容易。它会编译并运行,并且如果项目不是无关紧要的对象,您就已经不知所措了–您的迭代在迭代的每个步骤都调用了复制构造函数和析构函数。如果要在基于范围的迭代器中盲目使用auto,则它应该是“ for(const auto&item:some_container)”,而不是“ for(auto item:some_container)”。
唐·哈奇

2
并非总是如此,但是好的,您可能需要参考。所以呢?这与无关auto
spraff 2014年

我真的不明白您的最后评论。我试图解释一下为什么您的策略对我来说不是一个很好的策略,以及为什么我对此表示反对。
唐·哈奇

5
我的意思是,是否使用引用与是否使用自动正交。我将对其进行编辑以添加参考,因为诚然,这通常是人们想要做的,但它与手头的主题完全无关。
spraff 2014年

43

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

至少某些类型的程序员会过度使用任何语言的每项新功能。其余的有经验的程序员只有通过一些有经验的程序员(不是菜鸟)的适度过度使用,才能了解正确使用的范围。过度使用通常是不好的,但可能会很好,因为过度使用可能会导致功能的改进或更好的功能来替代它。

但是如果我使用的代码多于几行

auto foo = bla();

其中类型被指示为零次,我可能想更改这些行以包含类型。第一个例子很棒,因为类型只被声明了一次,从而使auto我们免于两次编写混乱的模板化类型的麻烦。Hooray for C ++++。但是,如果在附近的行中不容易看到该类型,则显式显示零次会使我感到紧张,至少在C ++及其直接后继版本中是如此。对于设计为在更高层次上具有更多抽象性,多态性和通用性的其他语言,这很好。


38

C ++和Beyond 2012的“ 任何问题”面板中,Andrei Alexandrescu,Scott Meyers和Herb Sutter之间auto进行了一次精彩的交流,讨论何时使用和不使用。跳至25:03分钟进行4分钟的讨论。三位发言者都指出了何时使用时要牢记的重点auto

我强烈鼓励人们得出自己的结论,但是我的收获是,除非在以下情况中使用它auto否则到处都是

  1. 损害可读性
  2. 担心自动类型转换(例如,从构造函数,赋值,模板中间类型,整数宽度之间的隐式转换)

自由使用explicit有助于减少对后者的担忧,这有助于最大程度地减少前者的问题时间。

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


4
也许还值得链接到“几乎总是自动的” -herbutter.com/2013/08/12/…–
罗布·

37

是的,可能会过度使用它,从而损害可读性。我建议在确切类型长,难以言说或对可读性不重要且变量寿命短的环境中使用它。例如,迭代器类型通常很长,并不重要,因此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();

2
当然,在语言中具有基于范围的for循环会使您的第一个示例不那么令人兴奋。8v)
Fred Larson

@弗雷德:类型仍然很麻烦(我在考虑关联容器)
Matthieu M.

3
@Fred:每当您的界限不是begin()end(),或者步长不是1时,或者您在循环时修改容器时,基于范围的for语句将无济于事。
丹尼斯·齐克福斯

6
@geotavros:r_and_t<r_and_t<r_char_t<char>&, r_char_t<char>&>, r_char_t<char>&>是吗?
丹尼斯·齐克福斯

7
@geotavros:或者您可以看到什么类型space,然后进行搜索。无论如何,这是更有用的信息...毕竟,问题不是“这个新变量是什么类型”,而是“什么space & space & space意思?” 表达式的实际类型只是噪音。
2011年

19

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.

1
首先,这似乎是奇怪的行为。如果我将两个矩阵相乘,即使操作是惰性的,我也不希望它可以重新评估,我希望它在初次评估后能保持其评估状态。如果要在不修改原始参数的情况下更改参数,那么是否最终还是必须重建表达式?还是为原始参数不断变化但过程保持不变的情况下的流处理设计?
2015年

1
无论A*B表达式是否复制到auto变量或其他变量中,您描述的行为仍然存在。
xtofl

没有错误是造成使用auto
Jim Balter

@JAB:它旨在支持操作之间的简化和协同作用,例如,diag(A * B)不必浪费时间来计算非对角线元素。
Ben Voigt

我还发现了类似“ for(auto o:vecContainer)”的代码,其中“ o”是一个重对象,每次都会被复制。我的建议是将其用于最初的意图,即模板(具有难以推论的准则)和费力的typedef。即使那样,您也必须小心并区分auto和auto&。
gast128

10

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

auto i = MyClass();

代替

MyClass i;

在这种情况下,我会说这是滥用auto关键字。

通常我不介意什么是对象的确切类型,我更感兴趣的是其fonctionality,并作为函数名一般说说他们返回的对象,auto不伤害:在例如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 )

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


1
@ChristianRau:没讽刺。请注意,我不建议使用auto i = MyClass()
rafak 2013年

3
后续行动:请参阅:herbutterutter.com/2013/06/13/…as_unsigned建议在此处甚至使用,例如auto w = widget{};
rafak 2013年

3

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时,无法使用未初始化的变量。如果我们开始使用auto,这是我们可以免费获得的主要优势。

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

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


2
是。并指定类型,您可以初始化为0i,0u,0l,0ul,0.0f,0.0,甚至是int(),unsigned(),double()等。–
Robinson

6
这种说法是荒谬的。您说的是“您不会忘记初始化程序,因为会遇到编译器错误”,但这需要您记住使用auto
Lightness Races in Orbit

1
@LightnessRacesinOrbit:我从Herb Sutter的谈话中意识到(学习)了这个概念。我从草药谈话中发现了合理/实用的建议,因此被认为可以与社区分享。
Mantosh Kumar 2015年

2
我认为这不是使用自动的好动机。只需打开编译器的警告标志以使用未初始化的变量并解决所有警告。这并不是说你应该使用auto-但你并不需要它aboid这个问题。
einpoklum

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

1

auto在需要推断类型的地方使用。如果您知道某些东西是整数,或者您知道它是一个字符串,则只需使用int / std :: string等。我不会担心“过度使用”语言功能,除非它变得荒谬可笑,或混淆代码。

无论如何我都是这样。


4
棘手的部分是“有意义”。程序员可以如此沉迷于编码的细节,他们对其他程序员(尤其是未来的维护者)失去了什么意义。
2011年

没错,尽管我认为这很容易分辨。如有疑问,请发表评论!
LainIwakura 2011年

1
使用类型是否有意义?
米哈伊尔(Mikhail)

一些开发人员会说应该总是推断出类型!
2011年

使用注释……还是只使用类型而无需注释?
user997112

1

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


0

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

接受的答案表明拇指以下规则:

auto一开始很难说如何写类型时就使用它,但是表达式右侧的类型很明显。

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

auto x = f();

是什么使这成为滥用的例子auto?是我对f()的返回类型的无知吗?好吧,如果我确实知道这可能确实有帮助,但是-这不是我的主要关注点。什么是更多的问题是xf()是毫无意义的。如果我们有:

auto nugget = mine_gold();

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

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

  • 您会感到变量名以及初始化/赋值表达式不能提供有关该语句正在执行的操作的足够信息。
  • 您会感到变量名与初始化/赋值表达式一起提供了有关类型应为“误导”的信息-即,如果您不得不猜测是什么而不是自动的话,您将能够做出猜测-它将是错误的假设,这种错误的假设会在代码的后面产生影响。
  • 您想强制使用其他类型(例如引用)。

并且:

  • 在替换auto为具体类型之前,最好给出一个有意义的名称(当然不包含类型名称)。

-3

我的痛苦经历之一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  

7
您在解释与auto有什么关系方面做得很糟糕。您创建了一个函数,然后将其打印出来了。
丹尼斯·齐克福斯

5
@iammilind:那和auto有什么关系?
R. Martinho Fernandes

7
我发现人们不太可能想()说到底。Lambda可以充当函数,而函数指针正是从那里来的。如果要立即调用它,为什么还要使用lambda?auto i = 0;效果很好。
R. Martinho Fernandes

4
您能否至少描述一个没有lambda 的情况auto x = []() { /* .... whatever goes in here ... */ }()auto x = /* .... whatever goes in here ... */;(即同一件事)更好?我发现这毫无意义,因为同样的原因auto x = 42 + y - 42也没有意义。
R. Martinho Fernandes

6
-1这不是汽车的故障。Lambda的类型不能拼写,因此auto是必需的,如果您忘记调用该函数,那么这就是您自己的注意事项!您可以随意将未调用的函数指针放入C printf中。
spraff 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.