![](/img/trans.png)
[英]Why does C++ allow but ignore the application of const to function types?
[英]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))
在第二行中, i
和end
属于同一类型,称为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)
{ ... }
当变量i
、 end
和x
范围仅限于for
循环时。 AFAIK,除非这些变量具有通用类型,否则无法在 C++ 中实现。 这是正确的吗? (排除将所有类型放入struct
丑陋技巧)
在一些可变参数模板应用程序中也可能有用例。
我认为这只是与非auto
声明的一致性问题。
这个:
auto n = 42, *p = &n;
相当于:
int n = 42, *p = &n;
其中int
和int*
类型是从初始值设定项派生的。 在这两种情况下,即使int
和int*
是不同的类型,由于它们密切的句法关系,它们被允许在同一个声明中。 (根据 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.