在C ++中用auto来声明variables是否有缺点?

似乎auto是一个相当重要的function,在C ++ 11中join,似乎遵循了很多新的语言。 就像Python这样的语言,我还没有看到任何显式的variables声明(我不确定是否有可能使用Python标准)。

使用auto声明variables而不是显式声明variables是否有缺点?

你只是问了一些缺点,所以我强调了其中的一些。 如果使用得当, auto也有一些优点。 弊端是由于易于滥用,以及代码以无意的方式行事的可能性增加。

主要的缺点是,通过使用auto ,您不一定知道正在创build的对象的types。 程序员也可能期望编译器推导出一种types,但编译器会坚定地推导出另一种types。

给定一个类似的声明

 auto result = CallSomeFunction(x,y,z); 

你不一定知道result是什么types。 这可能是一个int 。 这可能是一个指针。 这可能是别的。 所有这些支持不同的操作。 你也可以通过一些小改动来大幅改变代码

 auto result = CallSomeFunction(a,y,z); 

因为根据CallSomeFunction()存在的重载,结果的types可能会完全不同,因此后续代码的行为可能与预期完全不同。 您可能会突然在后面的代码中触发错误消息(例如,随后尝试解除引用int ,尝试更改现在为const东西)。 更险恶的变化是你的改变在编译器上航行的地方,但后来的代码performance出不同的和未知的 – 可能是越界的方式。

由于没有明确了解某些variables的types,所以很难严格certificate代码能够按预期工作。 这意味着需要更多的努力来certificate在高危(如安全关键或关键任务)领域“适合目的”的主张。

另一个更常见的缺点是,程序员的诱惑是使用auto作为一个钝器来强制代码编译,而不是去思考代码在做什么,并努力做到正确。

这完全不是原则性auto的缺点,但实际上这似乎是一个问题。 基本上,有些人要么:a)把auto看作是types的救星,并在使用auto时候closures他们的大脑;或者b)忘记auto总是推导出价值types。 这导致人们做这样的事情:

 auto x = my_obj.method_that_returns_reference(); 

哎呀,我们只是深刻地复制了一些对象。 这往往是一个错误或性能失败。 然后,你也可以摆动另一种方式:

 const auto& stuff = *func_that_returns_unique_ptr(); 

现在你得到一个悬而未决的参考。 这些问题不是由auto造成的,所以我不认为它们是合法的。 但是,看起来像auto这样的问题会让我更加常见(从我个人的经验来看),因为我在开始时列出的原因。

我想给人时间调整,理解分工: auto推导出底层的types,但是你仍然要考虑参考性和一致性。 但是需要一点时间。

其他答案提到的缺点就像“你不知道variables的types是什么”。 我想说,这主要与代码中草率的命名惯例有关。 如果您的界面名称清晰,则不需要关心具体的types。 当然, auto result = callSomeFunction(a, b); 并没有告诉你很多。 但是auto valid = isValid(xmlFile, schema); 告诉你足够的使用valid而不必关心它的确切types是什么。 毕竟,只是if (callSomeFunction(a, b)) ,你也不知道types。 与其他任何子expression式临时对象相同。 所以我不认为这是auto一个真正的缺点。

我会说它的主要缺点是,有时,确切的返回types不是你想要的。 实际上,有时实际返回types与“逻辑”返回types不同,因为实现/优化细节。 expression式模板是一个主要的例子。 假设我们有这个:

 SomeType operator* (const Matrix &lhs, const Vector &rhs); 

从逻辑上讲,我们希望SomeTypeVector ,我们当然希望在我们的代码中这样对待它。 不过,为了优化目的,我们使用的代数库实现了expression式模板,实际的返回types是这样的:

 MultExpression<Matrix, Vector> operator* (const Matrix &lhs, const Vector &rhs); 

现在,问题在于, MultExpression<Matrix, Vector>将很可能存储一个const Matrix&const Vector& internal; 它期望在它的完整expression式结束之前将它转换成Vector 。 如果我们有这个代码,一切都很好:

 extern Matrix a, b, c; extern Vector v; void compute() { Vector res = a * (b * (c * v)); // do something with res } 

但是,如果我们在这里使用auto ,我们可能会遇到麻烦:

 void compute() { auto res = a * (b * (c * v)); // Oops! Now `res` is referring to temporaries (such as (c * v)) which no longer exist } 

其中一个缺点是有时你不能用const_iterator声明const_iterator 。 在这个问题的代码示例中,您将获得普通(非const)迭代器:

 map<string,int> usa; //...init usa auto city_it = usa.find("New York"); 

它使你的代码更难读或者更乏味。 想象一下这样的事情:

 auto output = doSomethingWithData(variables); 

现在,要弄清楚输出的types,你需要跟踪doSomethingWithData函数的签名。

像这个开发者,我讨厌auto 。 或者说,我讨厌人们如何滥用auto

我(强烈的)认为, auto是帮助你编写通用代码, 而不是减less打字
C ++是一种语言,其目标是让您编写健壮的代码, 而不是最小化开发时间。
从C ++的许多特性来看,这是相当明显的,但不幸的是,一些像auto这样的新特性使得打字人员误以为他们应该开始懒于打字。

auto前几天,人们使用typedef ,这很好,因为typedef允许库的devise者帮你弄清楚返回types应该是什么样的,这样他们的库就能按预期工作。 当你使用auto ,你从这个类的devise器中 拿走了这个控制器 ,而不是让编译器弄清楚这个types应该是什么types的,它会从工具箱中删除一个function最强大的C ++工具,并冒着破坏代码的危险。

一般来说,如果你使用auto ,应该是因为你的代码适用于任何合理的types而不是因为你懒得写下它应该使用的types。 如果你使用auto作为一种工具来帮助懒惰,那么会发生什么呢?最终你会在你的程序中引入微妙的错误 ,通常是由于你使用了auto而没有发生的隐式转换引起的。

不幸的是, 这些错误在这里很难用一个简短的例子来说明 ,因为它们的简洁使得它们比用户项目中出现的实际例子更不具有说服力 – 然而,它们很容易出现在模板繁重的代码中,期望进行某些隐式转换地点。

如果你想要一个例子,这里有一个例子。 有一点需要注意的是,在被诱惑跳跃和批评代码之前,请记住,围绕这种隐含的转换,已经开发了许多知名和成熟的库, 它们在那里是因为它们解决了即使不是不可能也是困难的问题否则解决。 试图找出一个更好的解决scheme之前批评他们。

auto本身并没有缺点,我主张(手动)在新代码中到处使用它。 它允许你的代码持续地进行types检查,并始终避免无声的切片。 (如果BA派生,并且返回A的函数突然返回B ,则auto按预期存储其返回值)

虽然,C ++ 11之前的代码可能依赖于使用显式typesvariables引起的隐式转换。 将显式types的variables改为auto可能会改变代码行为 ,所以你最好谨慎行事。

关键字auto从返回值中简单地推导出types。 因此,它不等同于一个Python对象,例如

 # Python a a = 10 # OK a = "10" # OK a = ClassA() # OK // C++ auto a; // Unable to deduce variable a auto a = 10; // OK a = "10"; // Value of const char* can't be assigned to int a = ClassA{} // Value of ClassA can't be assigned to int a = 10.0; // OK, implicit casting warning 

由于在编译过程中auto导出,所以在运行时不会有任何缺陷。

到目前为止没有人提到,但是如果你问我的话,这本身就是值得回答的。

因为(即使每个人都应该知道C != C++ )用C != C++编写的代码很容易被devise为为C ++代码提供基础,因此devise时没有太多的努力来兼容C ++,这可能是devise的一个要求。

我知道一些规则,其中一些来自C定义良好的结构对C++无效,反之亦然。 但是这只会导致可执行文件被破坏,并且已知的UB子句适用于大多数时候被奇怪的循环引起的崩溃或者其他任何事情(甚至可能未被发现,但在这里并不重要)。

auto是第一次这个变化!

想象一下,您以前使用auto作为存储类说明符并转移代码。 它甚至不一定(取决于它被使用的方式)“突破”; 它实际上可以默默地改变程序的行为。

这是应该记住的。


1 至less我第一次意识到。

正如我在这个答案中所描述的, auto有时会导致你不想要的时髦情况。 你必须明确地说auto&有一个引用types,而只是auto可以创build一个指针types。 这可以通过一起省略说明符而导致混淆,导致引用的副本而不是实际的引用。

我能想到的一个原因是,你失去了强制返回class级的机会。 如果你的函数或方法返回一个长64位,而你只想要一个32位的unsigned int,那么你失去了控制它的机会。

另一个恼人的例子:

 for (auto i = 0; i < s.size(); ++i) 

生成一个警告( comparison between signed and unsigned integer expressions [-Wsign-compare] ),因为i是一个有符号的int。 为了避免这种情况,你需要编写例如

 for (auto i = 0U; i < s.size(); ++i) 

或者也许更好:

 for (auto i = 0ULL; i < s.size(); ++i) 

我认为auto在本地化环境中使用是很好的,读者很容易和明显地可以减less它的types,或者通过对其types的评论或者推断实际types的名称来logging。 那些不明白它是如何工作的人可能会采取错误的方式,如使用它而不是template或类似的。 在我看来,这里有一些好的和坏的用例。

 void test (const int & a) { // b is not const // b is not a reference auto b = a; // b type is decided by the compiler based on value of a // a is int } 

好用

迭代器

 std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int> v(); .. std::vector<boost::tuple<ClassWithLongName1,std::vector<ClassWithLongName2>,int>::iterator it = v.begin(); // VS auto vi = v.begin(); 

函数指针

 int test (ClassWithLongName1 a, ClassWithLongName2 b, int c) { .. } .. int (*fp)(ClassWithLongName1, ClassWithLongName2, int) = test; // VS auto *f = test; 

不好的用途

数据stream

 auto input = ""; .. auto output = test(input); 

function签名

 auto test (auto a, auto b, auto c) { .. } 

琐事

 for(auto i = 0; i < 100; i++) { .. }