为什么我需要明确写出'auto'关键字?

Far*_*hid 78 c++ auto c++11

我正在从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.

  • @Aconcagua - *"声明一个新变量(如果尚未存在)"*但是阴影*是*语言的一部分,并且仍然必须工作,就像Bathsheba说明的那样.这是一个比人们想象的更大的问题.它不是从头开始的语言设计,而是关于改变生活呼吸语言.要难得多了.有点像改变超速驾驶汽车的车轮. (34认同)
  • @liliscent - 是基于意见吗?(a)它已经是一个保留关键字.(b)意思很清楚.(c)它避免引入新的令牌(如`:=`).(d)它已经符合语法.我认为这里的意见空间很小. (23认同)
  • 输入完全相同的东西.区分分配和初始化需要在标准部分进行任意选择.既然我们已经有了"任何可能是声明的声明都是宣言"的规则,我们就会陷入非常模糊的水中. (8认同)
  • 这不是问题.像golang一样,你可以使用类似`n:= 0`的东西来引入一个新的变量.为什么使用"auto"是基于意见的问题. (4认同)
  • @StoryTeller如果`x = f()`声明一个新变量(如果尚未存在),获取f`s返回值的类型,则不需要新标记...要求auto显式声明变量,但是,降低意外声明新变量的风险(例如因为拼写错误......). (2认同)

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的评论,因为它的重要性 - 感谢暗示)

  • 在我看来,不要求"auto"的更大危险在于它意味着在远离函数的地方(例如在头文件中)添加一个全局变量可以转换为一个声明的声明.该函数中的本地范围变量转换为全局变量的赋值......可能带来灾难性(当然也非常令人困惑)的后果. (23认同)
  • 人们确实设法使用 Python,每次在赋值的左侧出现新名称时,Python 都会愉快地声明一个变量(即使该名称是拼写错误)。但我确实认为这是该语言更严重的缺陷之一。 (2认同)

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++实际上有两个占位符:autodecltype(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)).等等.

占位符的工作原因是它们是解决问题的最简单方法,同时保留了使用实际类型名称的所有好处和一般性.如果你想出了另一种像占位符一样普遍存在的替代方案,那么它就不太可能像占位符一样简单.

除非它只是用不同的关键字或符号拼写占位符......

  • 恕我直言,这是解决选择占位符背后的一些重要理由的唯一答案.通用lambda中的类型推导可能是另一个例子.遗憾的是,这个答案得到了很少的赞成,因为它的发布时间有点晚...... (2认同)

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()成为一个赋值语句,不再是一个声明.


cod*_*707 9

语法必须是明确的,也是向后兼容的.

如果auto被删除,则无法区分语句和定义.

auto n = 0; // fine
n=0; // statememt, n is undefined.
Run Code Online (Sandbox Code Playgroud)

  • 重要的一点是`auto`已经是一个关键字(但具有过时的含义),因此它没有使用它作为名称来破坏代码.这是一个原因,而不是选择更好的关键字,如`var`或`let`. (3认同)