我有这样的代码:
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;
,但在这种情况下,list是指向QList的指针,其size()方法返回int
,{{1} }它本身也应该是-1
。错误信息也有点奇怪。
变量int
和i
仅在此循环中需要,我想将它们声明为循环参数。输入j
而不是自动输入并不困难,但我想知道:int
不应该用于一次声明多个变量,或者我是在这里遗漏了一些东西,它确实是错误的代码,或者它可能是编译器的错误?
P.S。看起来使用模板函数是这里的关键部分,将循环从模板中分解出来不会产生错误。那么,更像是编译器中的错误?
答案 0 :(得分:13)
这是海湾合作委员会的一个错误。
根据[dcl.spec.auto] / 1:
auto
和decltype(auto)
类型指定者用于指定 占位符类型,稍后将通过从中扣除来替换 初始化。 [...]
模板参数推导的规则永远不会将类型推导为auto
。在这种情况下,扣除的目的实际上是使用推导类型替换 auto
。
在示例中,list
具有依赖类型(它取决于模板参数ListItem
),因此表达式list->size() - 1
也具有依赖类型,其类型为{ {1}}也是依赖的,这意味着只有在实例化函数模板i
时才会解析它。只有这样才能检查与该声明相关的其他语义约束。
根据[temp.res] / 8:
知道哪些名称是类型名称允许每个模板的语法 被检查。该程序格式错误,无需诊断,如果:
[......这里没有适用的案例列表......]
否则,不应为a的模板发出诊断 可以生成有效的专业化。 [注意:如果模板是 实例化后,错误将根据其他规则进行诊断 本标准。确切地说,当诊断出这些错误时,质量就是 实施问题。 - 结束记录]
(强调我的)
在分析模板printList
的定义时,GCC发出错误是错误的,因为可以生成明确有效的模板特化。事实上,如果printList
没有QList
返回size()
以外的其他内容的专业化,则int
和i
的声明将是在j
。
所有报价均来自N4606,(几乎)当前的工作草案,但上述报价的相关部分自C ++ 14以来未发生变化。
更新:GCC 6/7中的Confirmed as a regression。感谢T.C.提供错误报告。
更新:针对即将发布的6.4和7.0版本修复了原始错误(78693)。它还通过GCC处理此类构造的方式发现了一些其他问题,导致另外两个错误报告:79009和79013。
答案 1 :(得分:4)
正如我对your answer的评论所述,我同意你所提出的分析 最简单的问题形式(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()
。
现在,在上面的示例中,decltype(auto)
的{{1}}仍然是i
,因为依赖类型auto
未知。但是,T
肯定是j
。
因此,编译器错误有意义。当前行为(G ++> = 6),可能是也可能不是错误。这取决于我们对编译器的期望。 : - )
然而,这个错误不能被谴责。以下是C++17 draft的支持标准报价:
7.1.7.4.1占位符类型扣除
4 如果占位符是自动类型说明符,则使用模板参数推导规则确定推断类型T替换T。通过用新发明的汽车取代汽车的出现来从T获得P. 类型模板参数U
C++14 standard中的相同内容与7.1.6.4 / 7相同。
为什么在第一个模板检查中会报告此错误?
我们可能正确地争辩说,为什么编译器在第一次语法检查本身中如此“迂腐”。既然,我们没有实例化,那就不应该没问题!即使我们实例化,也不应该只为有问题的电话提供错误! 这就是g ++ - 5的作用。他们为什么要费心去改变呢?
我认为,这是一个有效的论点。使用g ++ - 5,如果我打电话:
int
当foo(1); // ok
foo(1.0); // error reported inside `foo()`, referencing this line
和i
属于不同类型时,编译器会正确报告错误及其层次结构。
答案 2 :(得分:1)
我随后将收到的有关该主题的信息进行夏季化。
示例代码中的问题在于使用模板函数。
编译器首先对模板进行泛型检查而不进行实例化,这意味着模型参数(以及依赖于它们的类型,如其他模板)的类型是未知的,如果它取决于那些未知的那些auto
类型再次推导到auto
(或者不推导成某种具体类型)。我从来没有觉得即使扣除后auto
仍然可以auto
。现在,原始编译器错误文本非常有意义:变量j
被推断为类型为int
,但变量i
在推导后仍为auto
。由于auto
和int
是不同的类型,编译器会生成错误。