我正在从C ++ 98转向C ++ 11,并熟悉auto
关键字。我想知道为什么我们需要显式声明auto
如果编译器能够自动推断出类型。我知道C ++是一种强类型语言,这是一条规则,但如果没有明确声明变量auto
,是不可能实现相同的结果?
答案 0 :(得分:154)
删除明确的auto
会破坏语言:
e.g。
int main()
{
int n;
{
auto n = 0; // this shadows the outer n.
}
}
您可以看到放弃auto
不会阴影外部n
。
答案 1 :(得分:36)
您的问题允许两种解释:
Bathsheba answered很好地是第一种解释,对于第二种解释,请考虑以下内容(假设到目前为止还没有其他声明存在; 假设有效的C ++):
int f();
double g();
n = f(); // declares a new variable, type is int;
d = g(); // another new variable, type is double
if(n == d)
{
n = 7; // reassigns n
auto d = 2.0; // new d, shadowing the outer one
}
它将可能,其他语言可以很好地消失(好吧,除了阴影问题)...然而,在C ++中并非如此,而且问题(在感觉第二种解释)现在是:为什么?
这一次,答案并不像第一次解释那样明显。但有一点是显而易见的:对关键字的明确要求使语言更安全(我不知道这是否是推动语言委员会做出决定的原因,仍然是一个重点):
grummel = f();
// ...
if(true)
{
brummel = f();
//^ uh, oh, a typo...
}
我们可以同意这一点,不需要任何进一步的解释吗?
不需要自动的更大危险[然而]是,它意味着在远离函数的地方添加一个全局变量(例如在头文件中)可以转换为什么意图是该函数中的本地范围变量到全局变量的赋值中......可能带来灾难性(当然也非常令人困惑)。
(引用psmears'评论由于其重要性 - 感谢提示)
答案 2 :(得分:14)
如果没有明确声明变量
,是否无法实现相同的结果auto
?
我会稍微改一下你的问题,以帮助你理解为什么需要auto
:
如果没有明确使用类型占位符,是否无法实现相同的结果?
不是可能吗?当然这是可能的"。问题是这样做是否值得。
其他语言中没有类型名称的大多数语法都以两种方式之一工作。这是Go-like方式,name := value;
声明变量。并且有类似Python的方式,name = value;
如果先前未声明name
,则声明一个新变量。
让我们假设在将任何一种语法应用于C ++时都没有语法问题(尽管我已经可以看到在C ++中identifier
后跟:
意味着"制作一个标签&#34)。那么,与占位符相比,你输了什么?
好吧,我再也不能这样做了:
auto &name = get<0>(some_tuple);
请参阅,auto
始终表示&#34;价值&#34;。如果您想获得参考,则需要明确使用&
。如果赋值表达式是prvalue,它将无法编译。基于赋值的语法都没有办法区分引用和值。
现在,如果给定值是引用,您可以使这样的赋值语法推导引用。但这意味着你无法做到:
auto name = get<0>(some_tuple);
这个从元组中复制,创建一个独立于some_tuple
的对象。有时,这正是你想要的。如果您想要使用auto name = get<0>(std::move(some_tuple));
从元组移动,这将更加有用。
好的,也许我们可以稍微扩展这些语法来解释这种区别。也许&name := value;
或&name = value;
意味着推断出像auto&
这样的引用。
decltype(auto) name = some_thing();
哦,没错; C ++实际上有two placeholders: auto
and decltype(auto)
。这种推论的基本思想是它的工作方式与完成decltype(expr) name = expr;
完全相同。所以在我们的例子中,如果some_thing()
是一个对象,它将推导出一个对象。如果some_thing()
是引用,则会推断出引用。
当您在模板代码中工作并且不确定函数的返回值是什么时,这非常有用。这非常适合转发,它是一个必不可少的工具,即使它没有被广泛使用。
所以现在我们需要在语法中添加更多内容。 name ::= value;
表示&#34;执行decltype(auto)
所做的事情&#34;。我不具备Pythonic变体的等价物。
看看这种语法,是不是很容易意外错误输入?不仅如此,它几乎不能自我记录。即使您以前从未见过decltype(auto)
,但它很大且非常明显,您至少可以轻松地告诉我们有什么特别的事情发生。而::=
和:=
之间的视觉差异很小。
但那个意见的东西;还有更多实质性问题。请参阅,所有这些都基于使用赋值语法。那么...... 无法使用赋值语法的地方呢?像这样:
for(auto &x : container)
我们是否将其更改为for(&x := container)
?因为这似乎是基于范围for
的非常不同。看起来它是来自常规for
循环的初始化语句,而不是基于范围的for
。它与未推断的案例的语法也不同。
此外,复制初始化(使用=
)在C ++中与直接初始化(使用构造函数语法)不同。因此name := value;
可能无效auto name(value)
。
当然,您可以声明:=
将使用直接初始化,但这与其余C ++的行为方式完全一致。
此外,还有一件事:C ++ 14。它给了我们一个有用的演绎功能:返回类型演绎。但这是基于占位符。与基于范围的for
非常相似,它基本上基于由编译器填充的类型名称,而不是基于应用于特定名称和表达式的某些语法。
请参阅,所有这些问题来自同一个来源:您正在发明用于声明变量的全新语法。基于占位符的声明并不需要发明新的语法。他们使用与以前完全相同的语法;他们只是使用一个类似行为的新关键字,但具有特殊含义。这使它能够在基于范围的for
和返回类型推导中工作。它允许它有多种形式(auto
与decltype(auto)
)。等等。
占位符的工作原理是因为它们是解决问题的最简单方法,同时保留了使用实际类型名称的所有好处和一般性。如果你想出了另一种像占位符一样普遍存在的替代方案,那么它就不太可能像占位符一样简单。
除非只是用不同的关键字或符号拼写占位符......
答案 3 :(得分:12)
auto
,但这会导致不一致。首先,正如所指出的,C ++中的声明语法是<type> <varname>
。显式声明需要一些类型或至少一个声明关键字。我们可以使用var <varname>
或declare <varname>
或其他内容,但auto
是C ++中长期存在的关键字,是自动类型扣除关键字的理想选择。
是否可以通过赋值隐式声明变量而不会破坏所有内容?
有时是的。您无法在函数外执行赋值,因此您可以在那里使用赋值语法进行声明。但是这种方法会使语言不一致,可能导致人为错误。
a = 0; // Error. Could be parsed as auto declaration instead.
int main() {
return 0;
}
当涉及到任何类型的局部变量时,显式声明是控制变量范围的方法。
a = 1; // use a variable declared before or outside
auto b = 2; // declare a variable here
如果允许使用含糊不清的语法,则声明全局变量可能会突然将本地隐式声明转换为赋值。查找这些转化需要检查所有内容。为了避免碰撞,你需要所有全局变量的唯一名称,这会摧毁整个范围的想法。所以它真的很糟糕。
答案 4 :(得分:11)
auto
是一个关键字,您可以在通常需要指定类型的地方使用该关键字。
int x = some_function();
通过自动推导出int
类型,可以使其更通用:
auto x = some_function();
所以这是对语言的保守扩展;它适合现有的语法。没有它x = some_function()
就成了一个赋值语句,不再是一个声明。
答案 5 :(得分:9)
语法必须是明确的,并且也是向后兼容的。
如果删除auto,则无法区分语句和定义。
auto n = 0; // fine
n=0; // statememt, n is undefined.
答案 6 :(得分:3)
添加到之前的答案中,来自旧屁的一个额外注释:看起来您可能会认为只是开始使用新变量而不以任何方式声明它是一个优势。
在可能隐式定义变量的语言中,这可能是一个大问题,尤其是在较大的系统中。你犯了一个拼写错误并调试了几个小时才发现你无意中引入了一个值为零(或更差)的变量 - blue
vs bleu
,label
vs {{1} } ...结果是,如果没有彻底检查精确的变量名,你就无法真正信任任何代码。
使用lable
告诉编译器和维护者你打算声明一个新变量。
想一想,为了能够避免这种噩梦,隐含的没有噩梦。声明在FORTRAN中引入 - 您现在看到它在所有严肃的FORTRAN程序中使用。没有它只是......可怕。