繁体   English   中英

为什么 C++ 不允许在一个 auto 语句中包含多种类型?

[英]Why does C++ not allow multiple types in one auto statement?

2011 C++ 标准引入了新的关键字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)
{ ... }

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

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

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

这个:

auto n = 42, *p = &n;

相当于:

int n = 42, *p = &n;

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

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

auto n = 42, x = 1.5;

但上述内容必须等效于两个单独的声明:

int n = 42; double x = 1.5;

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

即使没有auto ,您也可以在for循环标头中定义一个int和一个int*

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

但是你不能同时声明一个int和一个double 添加auto并没有改变这一点。

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

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

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

根据此功能已接受的提案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,他们认为它会更难实现,但另一个原因可能是它被丢弃,因为他们无法就何时选择不同类型(新行为)或何时隐式转换为推导类型达成共识第一个声明的变量(旧行为)。

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

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

这将 i 声明为整数 42,将 s 声明为以零结尾的字符串“Hello World”的const char* ,将 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);

这在 C++17 和 C++20 模式下用g++-10编译得很好,尽管它实际上应该只在 C++20 模式下编译。 C++17 不允许对别名模板进行模板参数推导,即使在最简单的情况下也是如此,其中模板参数列表以 1:1 的比例转发给别名模板。

如果要参考 C++ 2014 工作草案,则标准允许此类代码。这是标准草案中的示例

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

我想补充一点,在您的示例中 auto 不能被废弃,因为迭代器类型和迭代器的值类型是两种不同的类型。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM