考虑以下计划:
extern int x;
auto x = 42;
int main() { }
Clang 3.5接受它(live demo),GCC 4.9和VS2013不接受(live demo for the former)。谁是对的,C ++标准中指定的正确行为在哪里?
答案 0 :(得分:24)
关于这一点,标准中的惊人之处很少。关于重新声明的所有内容都是:
[C++11: 3.1/1]:
声明(第7条)可以将一个或多个名称引入翻译单元或重新声明先前声明引入的名称。 [..]
和auto
语义的唯一相关部分:
[C++11: 7.1.6.4/3]:
否则,变量的类型是从其初始化程序推导出来的。 [..]
(提醒我们x
的类型为int
)。
我们知道所有声明都必须为变量赋予相同的类型:
[C++11: 3.5/10]:
在对类型进行所有调整之后(其中typedefs(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的目的是拒绝该程序。但是,这是一个不同的特征(因为演绎不能仅适用于声明),因此是一个不同的场景。
无论哪种方式,改进的标准措辞都会有所帮助。我认为这是一个[相当小的]编辑缺陷。
答案 1 :(得分:1)
我answered一个问题已被关闭,与此问题重复。我要求合并,却被告知provide an answer here。请参阅下面的原始答案。
我在twitter上问了这个问题,response I received from 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是正确的,而且格式不正确。
答案 2 :(得分:0)
我认为[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.