使用 C++11 auto 关键字声明两个(或更多)变量
Using C++11 auto keyword to declare two (or more) variables
我有这样的代码:
template<class ListItem>
static void printList(QList<ListItem>* list)
{
for (auto i = list->size() - 1, j = -1; i >= 0; --i) {
std::cout << i << ", " << j << ": " << list->at(i) << std::endl;
}
}
当我用 g++ 6.2.1 编译它时,我得到以下编译器输出:
test.cpp: In function ‘void printList(QList<T>*)’:
test.cpp:10:7: error: inconsistent deduction for ‘auto’: ‘auto’ and then ‘int’
for (auto i = list->size() - 1, j = -1; i >= 0; --i) {
^~~~
我会理解这一点,如果变量有不同的类型,如 auto i = 0.0, j = 0;
,但在这种情况下,列表是指向 QList 及其 size() 方法的指针 returns int
, -1
本身也应该是 int
。报错信息也有点奇怪。
变量i
和j
只在这个循环中需要,我想将它们声明为循环参数。键入 int
而不是 auto 并不难,但我想知道: auto
不应该用于一次声明多个变量,或者我在这里遗漏了一些东西,它真的是错误代码,还是编译器的bug?
P.S。看起来使用模板函数是这里的关键部分,将循环排除在模板之外不会产生错误。所以,更像是编译器中的错误?
然后我将总结收到的关于该主题的信息。
示例代码中的问题在于使用模板函数。
编译器首先对模板进行通用检查而不实例化它,这意味着作为模板参数的类型(以及依赖于它们的类型,就像其他模板一样)是未知的,并且 auto
如果它依赖于那些未知的types 再次推导为 auto
(或不推导为某种具体类型)。在我看来,即使在扣除后 auto
仍然可以是 auto
。现在原始编译器错误文本非常有意义:变量 j
被推导为类型 int
,但变量 i
推导后仍为 auto
。由于 auto
和 int
是不同的类型,编译器会生成错误。
正如我在 的评论中所提到的,我同意您的分析。
问题的最简单形式 (demo):
template<class T>
void foo (T t) {
auto i = t, j = 1; // error: inconsistent deduction for ‘auto’: ‘auto’ and then ‘int’
}
int main () {}
在模板的情况下,编译器在其第一阶段检查基本语法而不实例化它。在我们的例子中,无论如何我们永远不会调用 foo()
。
现在,在上面的示例中,i
的 decltype(auto)
仍然是 auto
,因为依赖类型 T
未知。但是,j
肯定是int
。
因此,编译器错误是有道理的。目前的行为 (G++ >= 6),可能是也可能不是错误。这取决于我们对编译器的期望。 :-)
但是,这个错误不能谴责。这是来自 C++17 draft 的支持标准引用:
7.1.7.4.1 Placeholder type deduction
4 If the placeholder is the auto type-specifier, the deduced type T replacing T is determined using the rules for template argument deduction. Obtain P from T by replacing the occurrences of auto with either a new invented
type template parameter U
C++14 standard 中存在与 7.1.6.4 / 7 相同的内容。
为什么第一次模板检查本身会报告此错误?
我们可能会正确地争论,为什么编译器在第一个语法检查中如此 "pedantic"。既然,我们不是实例化,那岂不是应该可以吧!即使我们实例化,它不应该只对有问题的调用给出错误吗?
这就是 g++-5 所做的。为什么他们费心去改变它?
我认为,这是一个有效的论点。使用 g++-5,如果我调用:
foo(1); // ok
foo(1.0); // error reported inside `foo()`, referencing this line
当i
和j
是不同类型时,编译器会正确报告错误及其层次结构。
这是 GCC 中的错误。
根据[dcl.spec.auto]/1:
The auto
and decltype(auto)
type-specifiers are used to designate
a placeholder type that will be replaced later by deduction from an
initializer. [...]
模板参数推导规则永远不会将类型推导为 auto
。这种情况下推导的目的其实是用推导的类型代替auto
。
例子中,list
有一个依赖类型(它依赖于模板参数ListItem
),所以表达式list->size() - 1
也有一个依赖类型,这使得类型i
的也依赖,这意味着它只会在函数模板 printList
实例化时解析。只有这样才能检查与该声明相关的其他语义约束。
根据[temp.res]/8:
Knowing which names are type names allows the syntax of every template
to be checked. The program is ill-formed, no diagnostic required, if:
[... long list of cases of which none applies here ...]
Otherwise, no diagnostic shall be issued for a template for which a
valid specialization can be generated. [ Note: If a template is
instantiated, errors will be diagnosed according to the other rules in
this Standard. Exactly when these errors are diagnosed is a quality of
implementation issue. — end note ]
(强调我的)
GCC 在分析模板 printList
的定义时发出该错误是错误的,因为可以生成明确有效的模板特化。事实上,如果 QList
没有 size()
returns 除了 int
之外的任何特化,i
和 j
的声明将在 printList
.
的所有实例化中有效
所有引用都来自 N4606,(几乎)当前的工作草案,但上面引用的相关部分自 C++14 以来没有改变。
更新:Confirmed as a regression in GCC 6 / 7. Thanks to T.C. 错误报告。
更新:原始错误(78693) was fixed for the upcoming 6.4 and 7.0 releases. It also uncovered some other issues with the way GCC handles such constructs, resulting in two other bug reports: 79009 and 79013。
我有这样的代码:
template<class ListItem>
static void printList(QList<ListItem>* list)
{
for (auto i = list->size() - 1, j = -1; i >= 0; --i) {
std::cout << i << ", " << j << ": " << list->at(i) << std::endl;
}
}
当我用 g++ 6.2.1 编译它时,我得到以下编译器输出:
test.cpp: In function ‘void printList(QList<T>*)’:
test.cpp:10:7: error: inconsistent deduction for ‘auto’: ‘auto’ and then ‘int’
for (auto i = list->size() - 1, j = -1; i >= 0; --i) {
^~~~
我会理解这一点,如果变量有不同的类型,如 auto i = 0.0, j = 0;
,但在这种情况下,列表是指向 QList 及其 size() 方法的指针 returns int
, -1
本身也应该是 int
。报错信息也有点奇怪。
变量i
和j
只在这个循环中需要,我想将它们声明为循环参数。键入 int
而不是 auto 并不难,但我想知道: auto
不应该用于一次声明多个变量,或者我在这里遗漏了一些东西,它真的是错误代码,还是编译器的bug?
P.S。看起来使用模板函数是这里的关键部分,将循环排除在模板之外不会产生错误。所以,更像是编译器中的错误?
然后我将总结收到的关于该主题的信息。
示例代码中的问题在于使用模板函数。
编译器首先对模板进行通用检查而不实例化它,这意味着作为模板参数的类型(以及依赖于它们的类型,就像其他模板一样)是未知的,并且 auto
如果它依赖于那些未知的types 再次推导为 auto
(或不推导为某种具体类型)。在我看来,即使在扣除后 auto
仍然可以是 auto
。现在原始编译器错误文本非常有意义:变量 j
被推导为类型 int
,但变量 i
推导后仍为 auto
。由于 auto
和 int
是不同的类型,编译器会生成错误。
正如我在
问题的最简单形式 (demo):
template<class T>
void foo (T t) {
auto i = t, j = 1; // error: inconsistent deduction for ‘auto’: ‘auto’ and then ‘int’
}
int main () {}
在模板的情况下,编译器在其第一阶段检查基本语法而不实例化它。在我们的例子中,无论如何我们永远不会调用 foo()
。
现在,在上面的示例中,i
的 decltype(auto)
仍然是 auto
,因为依赖类型 T
未知。但是,j
肯定是int
。
因此,编译器错误是有道理的。目前的行为 (G++ >= 6),可能是也可能不是错误。这取决于我们对编译器的期望。 :-)
但是,这个错误不能谴责。这是来自 C++17 draft 的支持标准引用:
7.1.7.4.1 Placeholder type deduction
4 If the placeholder is the auto type-specifier, the deduced type T replacing T is determined using the rules for template argument deduction. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U
C++14 standard 中存在与 7.1.6.4 / 7 相同的内容。
为什么第一次模板检查本身会报告此错误?
我们可能会正确地争论,为什么编译器在第一个语法检查中如此 "pedantic"。既然,我们不是实例化,那岂不是应该可以吧!即使我们实例化,它不应该只对有问题的调用给出错误吗?
这就是 g++-5 所做的。为什么他们费心去改变它?
我认为,这是一个有效的论点。使用 g++-5,如果我调用:
foo(1); // ok
foo(1.0); // error reported inside `foo()`, referencing this line
当i
和j
是不同类型时,编译器会正确报告错误及其层次结构。
这是 GCC 中的错误。
根据[dcl.spec.auto]/1:
The
auto
anddecltype(auto)
type-specifiers are used to designate a placeholder type that will be replaced later by deduction from an initializer. [...]
模板参数推导规则永远不会将类型推导为 auto
。这种情况下推导的目的其实是用推导的类型代替auto
。
例子中,list
有一个依赖类型(它依赖于模板参数ListItem
),所以表达式list->size() - 1
也有一个依赖类型,这使得类型i
的也依赖,这意味着它只会在函数模板 printList
实例化时解析。只有这样才能检查与该声明相关的其他语义约束。
根据[temp.res]/8:
Knowing which names are type names allows the syntax of every template to be checked. The program is ill-formed, no diagnostic required, if:
[... long list of cases of which none applies here ...]
Otherwise, no diagnostic shall be issued for a template for which a valid specialization can be generated. [ Note: If a template is instantiated, errors will be diagnosed according to the other rules in this Standard. Exactly when these errors are diagnosed is a quality of implementation issue. — end note ]
(强调我的)
GCC 在分析模板 printList
的定义时发出该错误是错误的,因为可以生成明确有效的模板特化。事实上,如果 QList
没有 size()
returns 除了 int
之外的任何特化,i
和 j
的声明将在 printList
.
所有引用都来自 N4606,(几乎)当前的工作草案,但上面引用的相关部分自 C++14 以来没有改变。
更新:Confirmed as a regression in GCC 6 / 7. Thanks to T.C. 错误报告。
更新:原始错误(78693) was fixed for the upcoming 6.4 and 7.0 releases. It also uncovered some other issues with the way GCC handles such constructs, resulting in two other bug reports: 79009 and 79013。