名称空间作用域构造函数定义是否需要类限定的标识符

Does a namespace-scope constructor definition require a class-qualified identifier?

本文关键字:标识符 是否 空间 作用域 构造函数 定义      更新时间:2023-10-16

这是我们在c++的第一天学到的东西,我们认为这是理所当然的,但并没有明确遵循标准的措辞。

给定一个类S,可以定义它的构造函数

struct S { S(); };
S::S() { … }

但是标准似乎也允许这样做:

struct S { S(); };
S() { … }

用自身限定类名总是允许的,但总是多余的。例如,S::S::S::S() { … }也是一个有效的声明。如果S::S是,为什么不是普通的S ?

源自c++ 11§12.1/1,

构造函数没有名称。使用特殊的声明器语法来声明或定义构造函数。语法使用:

—可选的decl-specifier-seq,其中每个decl-specifier要么是函数说明符,要么是constexpr,

—构造函数的类名,和

-参数列表

这同样适用于类或命名空间作用域。有一个关于命名空间作用域的特殊规则,§9.3/5,

如果成员函数的定义在词法上超出其类定义,则成员函数名应使用::操作符由其类名限定。

但是,构造函数没有名称,所以这个不适用,对吗?此外,没有理由要求限定,因为没有语法歧义。在当前观察到的规则下,没有返回类型和标识符的类名声明的函数总是语法错误。对吧?

并不是说我们应该开始写省略限定符的代码,而是没有编译器接受这一点的原因,或者这只是传统?

是的,它说,

如果成员函数的定义在词法上超出其类定义成员函数名应使用::操作符由类名限定。

但是它没有说没有名字的成员函数不能被它的类名限定。不是吗?;)

这似乎导致了一个依赖于实现的不确定区域。但是,A::A的形式是由标准定义的。

5.1主表达式

如果使用了class-name:: class-name,并且两个类名指向同一个类,则用这种表示法命名构造函数。

至于是否允许A(){..},我想没有理由按照惯例这样做(是否有任何c++编译器允许它??)AFAIK不):

  1. 由于constructor是一个特殊的成员函数,所以A::A(){..}的方式与其他成员函数更加一致。为什么要让它表现得特别呢?

  2. 没有人愿意冒险去编写标准中没有明确规定的不兼容代码。

当在命名空间范围内面对令牌S() { }时,编译器不能神奇地确定它是一个actor。哪个语法规则会产生这样的符号序列?让我们忽略函数定义之外的所有内容;他们不能生产( ){ }部件。

这意味着S()必须是声明符,并且decl-specifier-seqopt必须为空(参见§8.4.1)。§9.2/7随后告诉我们,声明符必须命名构造函数、析构函数或转换函数。但S没有点名。因此,S() { }无效

虽然这个问题在最初发布的c++ 11标准中充其量是模棱两可的,在这个问题出现的时候,缺陷决议1435在2013年4月被接受,改变了相关文本,以便在命名空间范围内的构造函数定义确实需要一个限定id(使用::的语法)作为"名称"。

在c++ 14(或接近那个时间)中相同的段落是:

构造函数没有名称。构造函数的声明使用形式为

的函数声明符([dcl.fct])

ptr-declarator ( 参数声明子句 ) exception-specificationoptattribute-specifier-seqopt

,其中ptr声明符仅由id-expression、可选的属性指定符-seq和可选的环绕括号组成,并且id-expression具有以下形式之一:

  • 在属于类的成员规范但不是友元声明([class.friend])的成员声明中,id-expression是直接封闭类的注入类名([class]);
  • 在属于类模板的成员规范但不是友元声明的成员声明中,id-expression是一个类名,它命名了立即封闭的类模板的当前实例化([temp. depth .type]);或
  • 在命名空间范围的声明或友元声明中,id-表达式是一个限定id,用于命名构造函数([class. equal])。

这一段有一些更小的调整,没有改变这个问题的答案。当前版本为:

如果声明器是形式为

的函数声明器([dcl.fct]),则声明构造器

ptr-declarator ( 参数声明子句 ) noexcept-specifieroptattribute-specifier-seqopt

,其中ptr声明符仅由id-expression、可选的属性指定符-seq和可选的环绕括号组成,并且id-expression具有以下形式之一:

  • 在友元声明([class.friend])中,id-表达式是一个限定id,用于命名构造函数([class. equal]);
  • 否则,在属于类或类模板的成员规范成员声明中,id-表达式是直接封闭实体的注入类名([class.pre]);
  • 否则,id-expression是一个限定id,其unqualified-id是其查找上下文的注入类名。

构造函数没有名称. ...