13

2011 C++标准引入了new关键字auto,可以用来定义变量而不是类型,即

auto p=make_pair(1,2.5);                   // pair<int,double>
auto i=std::begin(c), end=std::end(c);     // decltype(std::begin(c))

在第二行中,iend属于同一类型,称为auto。标准不允许

auto i=std::begin(container), e=std::end(container), x=*i;

什么时候x会是不同的类型。我的问题:为什么标准不允许最后一行?可以通过将其解释auto为不表示某些要推导的类型,而是指示声明的任何变量的类型auto应从其分配的值推导出来来允许。C++11 标准是否有充分的理由不遵循这种方法?

实际上有一个用例,即在for循环的初始化语句中:

for(auto i=std::begin(c), end=std::end(c), x=*i;  i!=end;  ++i, x+=*i)
{ ... }

当变量iend和的范围x被限制在for循环中时。AFAIK,除非这些变量具有通用类型,否则这无法在 C++ 中实现。它是否正确?(将所有类型放入struct排除项的丑陋技巧)

在某些可变参数模板应用程序中也可能存在用例。

4

4 回答 4

9

我认为这只是与非auto声明的一致性问题。

这:

auto n = 42, *p = &n;

相当于:

int n = 42, *p = &n;

其中类型intint*派生自初始值设定项。在这两种情况下,即使intint*是不同的类型,由于它们密切的句法关系,它们也被允许在同一个声明中。(根据 C 和 C++ 声明几乎遵循的“声明遵循使用”规则,您将nand定义*p为 type int。)

在同一个声明中允许不相关的类型是可能的:

auto n = 42, x = 1.5;

但以上必须等同于两个单独的声明:

int n = 42; double x = 1.5;

我认为添加时的想法auto是对语言进行最小的更改,允许从初始化程序推断类型,但不更改可能的声明类型。

即使没有,您也可以在循环头中auto定义 anint和 an :int*for

for (int n = 42, *p = &n; expr1; expr2) { /* ... / }

但是你不能同时声明 anint和 a double。添加auto根本没有改变这一点。

在循环的上下文之外,for无论如何使用单独的声明通常要好得多。for在大多数情况下,将许多不同的声明放入一个循环中可以说是一个坏主意。对于需要大量声明的(可能很少见)情况,您可以将它们放在循环之上,如下所示:

auto i=std::begin(c), end=std::end(c),
for( x=*i;  i!=end;  ++i, x+=*i) {
    // ...
}

{ }如果要限制范围,请在整个事物周围添加另一组。(在这种情况下,您可能无论如何都想end这样const做。)

于 2013-10-31T21:50:11.947 回答
5

根据该功能已接受提案的最终修订版N1737,可能的多声明符auto实现如下:(来自第 6 节)

我们相信有可能实现一致的形式和一致的行为。我们通过插入(为了说明)中间 __Deduced_type 定义,并在 as-if 扩展中一致地应用此类型来做到这一点:

  // Listing 12
 typedef int __Deduced_type; // exposition only
  __Deduced_type a = 1;
 // decltype(a) is int
 __Deduced_type b = 3.14; // decltype(b) is int
  __Deduced_type * c = new float; // error; decltype(c) would be int *

通过这种协调一致的表述,我们不仅实现了形式和行为的一致性,而且还解决了更复杂的情况。例如,当前导声明符包含 ptr-operator 时:

 // Listing 13 
auto * a = new int(1), b = 3.14, * c = new float;

我们的公式附加了语义,就像声明的一样:

// Listing 14 
 typedef int __Deduced_type; // exposition only
 __Deduced_type * a = new int(1); // decltype(a) is int *
 __Deduced_type b = 3.14; // decltype(b) is int
 __Deduced_type * c = new float; // error; decltype(c) would be int *

正如这个可能的实现所示,更改类型将是无效的,因此会导致错误。
此功能以这种方式实现,因为否则它将与其他类型的多变量声明不同。
我记得看到过关于是否允许更改类型的讨论,但我不记得在哪里。IIRC,他们认为它会更难实现,但另一个原因可能是它被放弃了,因为他们无法就何时选择不同类型(新行为)或何时隐式转换为推断类型达成共识第一个声明的变量(旧行为)。

于 2013-10-31T16:10:31.067 回答
1

C++17 终于带来了结构化绑定声明,它解决了你的问题:

auto [ i, s, f ] = std::tuple(42, "Hello World", 5.0);

这将 i 声明为整数 42,将 s 声明为const char*以零结尾的字符串“Hello World”,将 f 声明为双精度数 5.0。

我不知道没有中间的解决方案std::tuple。如果您使用完全符合 C++20 的编译器,您还可以使用别名模板来缩写,std::tuple如下例所示:

template<typename ... ARGS> using T = std::tuple<ARGS ...>;
auto [ i, s, f ] = T(42, "Hello World", 5.0);

g++-10在 C++17 和 C++20 模式下都可以正常编译,尽管它实际上应该只在 C++20 模式下编译。C++17 不允许别名模板的模板参数推导,即使在最简单的情况下,模板参数列表被 1:1 转发给别名模板也是如此。

于 2021-03-11T15:58:55.573 回答
-2

如果要参考 C++ 2014 工作草案,那么标准允许这样的代码。这是标准草案中的一个例子

auto x = 5, *y = &x; // OK: auto is int

我想在您的示例中附加 auto can not be dedused 因为迭代器类型和迭代器的值类型是两种不同的类型。

于 2013-10-31T13:08:08.220 回答