使用 "auto" 的声明是否与使用具体类型说明符的外部声明匹配?



考虑以下程序:

extern int x;
auto x = 42;
int main() { }

Clang 3.5接受它(现场演示),GCC 4.9和VS2013不接受(前者的现场演示)。谁是对的,C++标准中规定的正确行为在哪里?

令人惊讶的是,标准中几乎没有关于这方面的内容。我们听到的关于重新申报的信息是:

[C++11: 3.1/1]:声明(第7条)可以将一个或多个名称引入翻译单元,或者重新声明由以前的声明引入的名称[..]

auto语义中唯一相关的部分:

[C++11: 7.1.6.4/3]:否则,变量的类型是从其初始值设定项推导出来的[..]

(提醒我们x的类型是int)。

我们知道,所有声明都必须为变量指定相同的类型:

[C++11: 3.5/10]:在对类型进行所有调整后(在此期间,typedef(7.1.3)被其定义所取代),所有引用给定变量或函数的声明所指定的类型应相同,除了数组对象的声明可以指定因存在或不存在主数组绑定而不同的数组类型(8.3.4)。违反此类型标识规则不需要诊断。

而"类型调整后"应该考虑到auto参与这一切的任何问题;因此,我的解释是,这本质上是x在全局范围内类型为int的有效重新声明(和定义),Clang是正确的。即使我们建议auto不算作"类型调整",因为不需要诊断,最坏的情况下所有列出的实现都以自己的方式兼容。

我相信GCC和Visual Studio的灵感来源于以下内容:

[C++11: 7.1.6.4/5]:在本节未明确允许的上下文中使用auto的程序是格式错误的。

…但我认为这是短视的。标准语言似乎不太可能仅仅因为7.1.6.4中没有重复或明确引用这些规则就禁止通常的重新声明规则。

C++14添加了与带有推导类型的函数声明相关的措辞:

[C++14: 7.1.6.4/13]:使用占位符类型的已声明返回类型的函数或函数模板的重新声明或专门化也应使用该占位符,而不是推导类型[..]

对称性可能表明,在int的情况下,GCC和VS拒绝程序是正确的。然而,这是一个不同的特性(因为推导不能仅应用于声明),因此是一个不一样的场景。

无论哪种方式,改进标准措辞都会有所帮助。我认为这是一个[相当小]的编辑缺陷。

注意

我回答了一个问题,这个问题是重复的。我要求合并,但被告知在这里提供答案。下面是我的原始答案。

更新clang是正确的

我在推特上问了这个问题,Richard Smith的回复如下:

这不是一个缺陷,此限制仅适用于推导的返回类型,而不适用于变量类型。对于变量,这只是一种方便的简写,但返回类型推导会影响函数(尤其是函数模板)的一些更基本的内容。

因此,逻辑是[dcl.spec.auto]允许这样做,并且为了限制推导的返回类型,在该节中添加了[dcl.spec.auto]p11段。否则就没有限制,因此这对于变量的情况是不受限制的。

原件

目前[dcl.spec.auto]似乎没有明确地涵盖这种情况,但它确实在[dcl.spec.auto]p5:中说明了这一点

在本款未明确允许的上下文中使用auto或decltype(auto)的程序格式不正确。

我们可以看到,它对[dcl.spec.auto]p11:中格式错误的函数也有类似的情况

函数或函数模板的重新声明或专业化带有使用占位符类型的声明返回类型的使用占位符,而不是推导类型。类似地,重新声明或函数或函数模板的特殊化不使用占位符类型的返回类型不应使用占位符。[示例:

auto f();
auto f() { return 42; } // return type is int
auto f(); // OK
int f(); // error, cannot be overloaded with auto f()

因此,尽管这可能需要澄清目前的措辞,但感觉gcc是正确的,这是不正确的。

我认为[dcl.spec.auto]p11中存在限制,因为否则,这将允许:

int f();
auto f(); // What's the return type here?

问题是,你可以有一个未减少的类型类型有一个函数的返回类型。没有基于以前声明的推导规则,这就是为什么函数不允许这样的混合,尽管以下内容完全可以:

int f();
auto f() { return 1; }

变量不存在此问题:

extern int v;
extern auto v; // ill-formed

任何仅声明的变量都必须使用非占位符类型。这意味着,如果在v的定义中使用占位符类型,则可以毫无问题地推导出它,然后当然必须匹配第一个声明中使用的非占位符类型。

extern int v;
auto v = 1; // ok, type deduced as 'int', matches first declaration.

最新更新