我一直在使用c++ 11标准中提供的新的auto关键字来处理复杂的模板类型,我相信它就是为这个目的设计的。但我也用它来做以下事情:

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

更令人怀疑的是:

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

我还没有看到很多关于这个话题的讨论。auto似乎被滥用了,因为类型通常是一种文档和完整性检查的形式。你在使用auto方面的界限在哪里,这个新功能的推荐用例是什么?

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


当前回答

我使用auto的一个痛苦经历是在lambda表达式中使用它:

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

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

你应该避免使用这样的表达式,并设置一个合适的返回类型(或受控的decltype())

上述例子的正确用法是:

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

其他回答

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

公认的答案包含以下经验法则:

当第一眼不知道如何写类型,但表达式右边的类型很明显时,请使用auto。

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

auto x = f();

是什么使这个例子滥用auto?是我不知道f()的返回类型吗?好吧,如果我知道的话可能会有帮助,但是-这不是我主要关心的。更大的问题是x和f()是没有意义的。如果我们有:

auto nugget = mine_gold();

相反,我通常不关心函数的返回类型是否明显。读这个语句,我知道我在做什么,我对返回值的语义有足够的了解,所以我不觉得我还需要知道它的类型。

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

You feel the variable name together with the initialization / assignment expression do not provide enough information about what the statement is doing. You feel the variable name together with the initialization / assignment expression provides "misleading" information about what the type should be - i.e., if you had to guess what comes instead of the auto you would be able to make a guess - and it would be wrong, and this false assumption has repercussions later in the code. You want to force a different type (e.g. a reference).

还有:

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

Auto关键字只能用于局部变量,不能用于参数或类/结构成员。所以,在任何你喜欢的地方使用它们都是安全可行的。我确实经常使用它们。类型在编译时推导,调试器在调试时显示类型,sizeof正确报告它,decltype将给出正确的类型——没有伤害。我从不认为auto是过度使用的!

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

这只是我的观点。

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

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

这里的Auto不会影响可读性。

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

   auto spaces = space & space & space;

with

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

一件容易的事。当你不关心是什么类型时使用它。例如

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

这里我只关心I是容器里的东西。

它有点像typedefs。

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

这里,我不关心h和w是浮点数还是双精度浮点数,只关心它们是任何适合表示高度和权重的类型。

或者考虑

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

这里我只关心它是一个合适的迭代器,支持运算符++(),在这方面有点像鸭子类型。

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

我真的无法想象对汽车的“滥用”。我能想到的最接近的是剥夺你自己到一些重要类型的显式转换——但你不会使用auto,你会构造一个所需类型的对象。

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

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

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

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