如果不明确声明变量auto,就不可能达到相同的结果吗?
我将稍微改一下你的问题,以帮助你理解为什么需要auto:
如果不显式使用类型占位符,是否不可能实现相同的结果?
这不是可能吗?当然这是“可能的”。问题是这样做是否值得。
其他语言中没有类型名的大多数语法以两种方式之一工作。有类似 Go 的方式,name := value; 声明一个变量。还有一种类似 Python 的方式,如果 name 之前没有声明过,name = value; 声明一个新变量。
让我们假设在 C++ 中应用这两种语法都没有语法问题(尽管我已经看到 identifier 后跟 : 在 C++ 中的意思是“制作标签”)。那么,与占位符相比,你失去了什么?
好吧,我不能再这样做了:
auto &name = get<0>(some_tuple);
看,auto 总是意味着“价值”。如果您想获得参考,您需要明确使用&。如果赋值表达式是纯右值,它将正确地无法编译。这两种基于赋值的语法都无法区分引用和值。
现在,如果给定值是引用,您可以使此类赋值语法推断引用。但这意味着你不能这样做:
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; 的意思是“做decltype(auto) 所做的事情”。我没有 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 vs. decltype(auto))。以此类推。
占位符之所以有效,是因为它们是解决问题的最简单方法,同时保留了使用实际类型名称的所有好处和通用性。如果您想出另一种与占位符一样通用的替代方案,那么它就不太可能像占位符那样简单。
除非它只是用不同的关键字或符号拼写占位符...