多少是太多的c++ 11 auto关键字?

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

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

更令人怀疑的是:

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

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

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

56022 次浏览

我认为应该使用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)

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

这只是我的观点。

大胆尝试吧。在任何地方使用auto可以使编写代码更容易。

任何语言中的每一个新特性都会被至少某些类型的程序员过度使用。只有通过一些有经验的程序员(不是新手)适度的过度使用,其他有经验的程序员才能学会正确使用的界限。过度使用通常是不好的,但也可能是好的,因为这种过度使用可能会导致功能的改进或更好的功能来取代它。

但如果我写的代码不止几行,比如

auto foo = bla();

其中类型被指示为0次,我可能想要更改这些行以包括类型。第一个例子很好,因为类型声明了一次,而auto使我们不必编写两次混乱的模板类型。C万岁++++。但是显式地显示类型0 times,如果它在附近的行中不容易看到,这让我感到紧张,至少在c++及其后续语言中是这样。对于其他设计在更高级别上工作的语言,具有更多的抽象性、多态性和泛型性,这是可以接受的。

是的,它可能被过度使用而损害可读性。我建议在这样的情况下使用它:确切的类型很长,或者无法表达,或者对可读性不重要,并且变量的寿命很短。例如,迭代器类型通常很长且不重要,因此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();

我使用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

可以在任何地方使用auto——尤其是const auto,这样副作用就不那么重要了。除了一些明显的情况外,您不必担心类型,但它们仍然会为您进行静态验证,并且可以避免一些重复。在auto不可行的地方,可以使用decltype根据表达式在语义上将类型表示为合同。您的代码看起来会有所不同,但这将是一个积极的变化。

我不受限制地使用auto,没有遇到任何问题。我甚至有时会将它用于简单的类型,如int。这使得c++对我来说是一种更高级别的语言,并且允许在c++中像在python中一样声明变量。在写完python代码之后,我有时甚至会写例如。

auto i = MyClass();

而不是

MyClass i;

这是我认为滥用auto关键字的一种情况。

通常我不介意对象的确切类型,我更感兴趣的是它的功能,而且函数名通常表示它们返回的对象的一些信息,auto没有坏处:在例如auto s = mycollection.size()中,我可以猜测s将是一种整数,在我关心确切类型的罕见情况下,让我们检查函数原型(我的意思是,我更喜欢在需要信息时检查,而不是在代码编写时先验检查,以防有一天它会有用,如int_type s = mycollection.size())。

关于这个例子,从公认的答案:

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

在我的代码中,在这种情况下我仍然使用auto,如果我想要x为unsigned,那么我使用一个名为say make_unsigned的实用函数,它清楚地表达了我的关注点:

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

免责声明:我只是描述我的的使用,我没有能力给出建议!

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

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

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

这里我所关心的是i是容器中的任何东西。

它有点像typedefs。

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

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

或者考虑

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

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

此外,lambdas的类型不能拼写,所以auto f = []...是很好的样式。另一种方法是强制转换为std::function,但这会带来开销。

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

如果你< em > < / em >在你的代码中删除了一些冗余,而没有引入副作用,那么这样做< em > < / em >必须是好的。

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

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

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

问我们任何问题面板的c++和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。”

我注意到的一个危险是在引用方面。 例如< / p >
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();

c++程序的主要问题之一是它允许你使用< em > < / em >未初始化变量。这导致了我们讨厌的非确定性程序行为。应该注意的是,现代编译器现在抛出适当的/message警告消息,如果程序累了使用它。

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

int main() {
int x;
int y = 0;
y += x;
}
如果我使用现代编译器(GCC)编译这个程序,它会给出警告。这样的警告可能并非如此 如果我们使用的是真正复杂的产品代码,这是非常明显的。< / p >

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

main.cpp:4:8: 警告: 'x'在此函数中未初始化 (-Wuninitialized) < / p >

Y += x;

    ^
< p >================================================================================= 现在,如果我们改变使用< em >汽车< / em >的程序,然后编译,我们得到以下结果:

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

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

main.cpp:2:10: 错误:声明'auto x'没有初始化式

 auto x;


^

如果我们开始使用< em >汽车< / em >,这是我们可能获得的主要优势(免费)。

c++专家草快门在他的CppCon14演讲中解释了这个概念和其他伟大的现代c++概念:

回归基本!现代c++风格要点

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.

由这类错误引起的bug是调试的一大麻烦。一种可能的补救方法是,如果执意要将auto用于从左到右的声明样式,则显式地将结果转换为预期的类型。

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

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

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

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

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

auto x = f();

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

auto nugget = mine_gold();

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

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

  • 您觉得变量名和初始化/赋值表达式并没有提供关于语句正在执行什么的足够信息。
  • 你觉得变量名和初始化/赋值表达式一起提供了关于应该是什么类型的“误导性”信息——也就是说,如果你必须猜测是什么而不是auto,你就可以做出猜测——这是错误的,这种错误的假设会在后面的代码中产生影响。
  • 你想强制一个不同的类型(例如引用)。

还有:

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

auto做什么?

它告诉编译器根据变量的初始值推断(确定)变量的数据类型。它使用类型演绎。

auto应该在哪里使用?

  • 当你不感兴趣知道变量的类型,只是

  • 当你想避免非常长和丑陋的类型名。

  • 当你不确定自己的类型。

  • 当你不想在你的代码中看到未初始化的变量。 Auto强制你初始化一个变量,因此你不能忘记做 < / p >。

当它不应该被使用或auto的缺点

    参考它的功能,auto可能会错误地推断type, One 这种情况是
 std::vector<bool> vec(10, 0);


auto x = vec[2];


bool y = vec[2];


std::cout << typeid(x).name() << "\n";


std::cout << typeid(y).name() << "\n";

g++ 10.2上的输出令人惊讶:

St14_Bit_reference


b
  • 如果你想让你的代码可读& 对其他人来说是可以理解的。它隐藏了数据类型的可见性