我正在从C++ 98转向C++ 11并熟悉该auto关键字.我想知道为什么我们需要显式声明auto编译器是否能够自动推断出类型.我知道C++是一种强类型语言,这是一个规则但是如果没有明确声明变量就不可能实现相同的结果auto吗?
Bat*_*eba 155
删除显式auto会破坏语言:
例如
int main()
{
int n;
{
auto n = 0; // this shadows the outer n.
}
}
Run Code Online (Sandbox Code Playgroud)
你可以看到掉落auto不会遮住外面的东西n.
Aco*_*gua 39
您的问题允许两种解释:
Bathsheba 很好地回答了第一种解释,对于第二种解释,考虑以下内容(假设到目前为止还没有其他声明; 假设有效的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
}
Run Code Online (Sandbox Code Playgroud)
这将是可能的,其他语言就完事很好用(当然,除了可能遮蔽问题)......也不是那么在C++中,虽然和问题(在第二个解释的意义上),现在是:为什么?
这一次,答案并不像第一次解释那样明显.但有一点是显而易见的:对关键字的明确要求使语言更安全(我不知道这是否是推动语言委员会做出决定的原因,仍然是一个重点):
grummel = f();
// ...
if(true)
{
brummel = f();
//^ uh, oh, a typo...
}
Run Code Online (Sandbox Code Playgroud)
我们可以同意这一点,不需要任何进一步的解释吗?
不需要自动的更大危险[然而]是,它意味着在远离函数的地方(例如在头文件中)添加全局变量可以转换为本地声明 - 将该函数中的作用域变量转换为对全局变量的赋值......可能带来灾难性(当然也非常令人困惑)的后果.
(引用psmears的评论,因为它的重要性 - 感谢暗示)
Nic*_*las 15
如果没有明确声明变量,是不是可以实现相同的结果
auto?
我将稍微改一下你的问题,以帮助你理解你需要的原因auto:
如果没有明确使用类型占位符,是否无法实现相同的结果?
是不是没有可能?当然这是"可能的".问题是这样做是否值得付出努力.
其他语言中没有类型名称的大多数语法都以两种方式之一工作.这是Go-like方式,name := value;声明变量.并且有类似Python的方式,name = value;如果name先前没有声明,则声明一个新变量.
让我们假设将任何一种语法应用于C++都没有语法问题(尽管我已经可以看到C++ identifier后面跟着:"制作标签").那么,与占位符相比,你输了什么?
好吧,我再也不能这样做了:
auto &name = get<0>(some_tuple);
Run Code Online (Sandbox Code Playgroud)
看,auto总是意味着"价值".如果要获取引用,则需要明确使用a &.如果赋值表达式是prvalue,它将无法编译.基于赋值的语法都没有办法区分引用和值.
现在,如果给定值是引用,您可以使这样的赋值语法推导引用.但那意味着你做不到:
auto name = get<0>(some_tuple);
Run Code Online (Sandbox Code Playgroud)
这从元组复制,创建一个独立的对象some_tuple.有时候,这正是你想要的.如果你想从元组移动,这将更有用auto name = get<0>(std::move(some_tuple));.
好吧,也许我们可以稍微扩展这些语法来解释这种区别.也许&name := value;或者&name = value;意味着要推断出类似的参考auto&.
好的.那这个呢:
decltype(auto) name = some_thing();
Run Code Online (Sandbox Code Playgroud)
哦,那是对的; C++实际上有两个占位符:auto和decltype(auto).这种推论的基本思想是,它的工作方式与您完成的方式完全相同decltype(expr) name = expr;.所以在我们的例子中,如果some_thing()是一个对象,它将推导出一个对象.如果some_thing()是参考,它将推断出参考.
当您使用模板代码并且不确定函数的返回值是什么时,这非常有用.这非常适合转发,它是一个必不可少的工具,即使它没有被广泛使用.
所以现在我们需要在语法中添加更多内容.name ::= value;意思是"做什么decltype(auto)".我没有Pythonic变种的等价物.
看看这种语法,是不是很容易意外错误输入?不仅如此,它几乎不能自我记录.即使你以前从未见过decltype(auto),它也足够明显,你至少可以轻易地告诉它有一些特别的事情发生.而之间的视觉差异::=和:=是最小的.
但那是意见的东西; 还有更多实质性问题.请参阅,所有这些都基于使用赋值语法.那么......你不能使用赋值语法的地方呢?像这样:
for(auto &x : container)
Run Code Online (Sandbox Code Playgroud)
我们改变它for(&x := container)吗?因为这似乎说的是与基于范围的非常不同的东西for.它看起来像是来自常规for循环的初始化语句,而不是基于范围的语句for.它与未推断的案例的语法也不同.
此外,复制初始化(使用=)在C++中与直接初始化(使用构造函数语法)不同.因此name := value;可能无法在可能的情况下工作auto name(value).
当然,您可以声明:=将使用直接初始化,但这与其余C++的行为方式完全一致.
还有,还有一件事:C++ 14.它给了我们一个有用的演绎功能:返回类型演绎.但这是基于占位符.与基于范围的一样for,它基本上基于由编译器填充的类型名称,而不是基于应用于特定名称和表达式的某些语法.
看,所有这些问题来自同一个来源:您正在发明用于声明变量的全新语法.基于占位符的声明不必发明新的语法.他们使用与以前完全相同的语法; 他们只是使用一个类似行为的新关键字,但具有特殊含义.这使它能够在基于范围的工作for和返回类型推导中工作.它允许它有多种形式(auto相对decltype(auto)).等等.
占位符的工作原因是它们是解决问题的最简单方法,同时保留了使用实际类型名称的所有好处和一般性.如果你想出了另一种像占位符一样普遍存在的替代方案,那么它就不太可能像占位符一样简单.
除非它只是用不同的关键字或符号拼写占位符......
And*_*nyy 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;
}
Run Code Online (Sandbox Code Playgroud)
当涉及到任何类型的局部变量时,显式声明是控制变量范围的方法.
a = 1; // use a variable declared before or outside
auto b = 2; // declare a variable here
Run Code Online (Sandbox Code Playgroud)
如果允许使用模糊语法,则声明全局变量可能会突然将本地隐式声明转换为赋值.找到这些转换需要检查所有内容.为了避免碰撞,你需要所有全局变量的唯一名称,这会摧毁整个范围的想法.所以这真的很糟糕.
rus*_*tyx 11
auto是一个关键字,您可以在通常需要指定类型的位置使用该关键字.
int x = some_function();
Run Code Online (Sandbox Code Playgroud)
通过int自动推导出类型可以使其更通用:
auto x = some_function();
Run Code Online (Sandbox Code Playgroud)
所以这是对语言的保守扩展; 它适合现有的语法.没有它就x = some_function()成为一个赋值语句,不再是一个声明.
语法必须是明确的,也是向后兼容的.
如果auto被删除,则无法区分语句和定义.
auto n = 0; // fine
n=0; // statememt, n is undefined.
Run Code Online (Sandbox Code Playgroud)