命名空间范围的构造函数定义是否需要类限定标识符?

发布于 2024-11-30 04:20:13 字数 863 浏览 0 评论 0原文

这是我们在 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,

如果成员函数的定义在词法上位于其类定义之外,则应使用 :: 运算符通过其类名来限定成员函数名称。

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

并不是说我们应该在省略限定条件的情况下开始编写代码,但是是否有任何编译器不接受这一点的原因,或者这只是传统?

Here's something we all learned on Day 1 of C++, which we take for granted but doesn't clearly follow from the wording of the Standard.

Given a class S, we can define its constructor

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

But the Standard seems to allow this just as well:

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

Qualifying the name of a class with itself is always allowed but always redundant. For example S::S::S::S() { … } is also a valid declaration. If S::S is, why not plain S?

From C++11 §12.1/1,

Constructors do not have names. A special declarator syntax is used to declare or define the constructor. The syntax uses:

— an optional decl-specifier-seq in which each decl-specifier is either a function-specifier or constexpr,

— the constructor’s class name, and

— a parameter list

in that order.

This applies equally to class or namespace scope. There is a special rule about namespace scope, §9.3/5,

If the definition of a member function is lexically outside its class definition, the member function name shall be qualified by its class name using the :: operator.

However, constructors do not have names, so this doesn't apply, right? Moreover, there's no reason to require the qualification, because there is no syntactic ambiguity. A function declared with no return type and a class-name for an identifier is always a syntax error under currently observed rules. Right?

Not that we should start writing code with the qualification omitted, but is there a reason that no compiler accepts this, or is it just tradition?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

木格 2024-12-07 04:20:13

是的,它说,

如果成员函数的定义在词法上位于其类定义之外
成员函数名称应使用 :: 运算符通过其类名称进行限定。

但它说没有名称的成员函数由它的类名限定。是吗? ;)

这似乎会导致一个不确定的领域,具体取决于实现。然而,A::A 的形式是由标准定义的。

5.1 主要表达式

如果使用 class-name :: class-name,并且两个类名引用同一个类,则此表示法命名构造函数。

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

  1. 由于构造函数是一个特殊的成员函数,因此 A::A( 的方式){..} 更多与其他成员功能一致。为什么要让它表现得特别呢?这可能不值得。

  2. 没有人愿意冒编写标准中未明确规定的不合规代码的风险。

Yes, it says that,

If the definition of a member function is lexically outside its class definition
the member function name shall be qualified by its class name using the :: operator.

But it doesn't says that member function w/o name shall not be qualified by its class name. Does it? ;)

That seems to lead to an uncertain area depending on implementations. However, the form of A::A is defined by the Standard.

5.1 Primary Expressions

Where class-name :: class-name is used, and the two class-names refer to the same class, this notation names the constructor..

As to whether A(){..} is allowed or not, I guess there is no reason to do it conventionally(Is there ANY C++ compiler allow it?? AFAIK, nope):

  1. Since constructor is a special member function, the way of A::A(){..} is more consistent with other member functions. Why borther allow it to behave specially? That's probably not worth the effort.

  2. No one wants to run the risk of writing non-compliant code that's not explicitly stated in the Standard.

我乃一代侩神 2024-12-07 04:20:13

当在命名空间范围内遇到标记 S() { } 时,编译器无法神奇地确定它是一个 ctor。哪种语法规则会产生这样的标记序列?让我们忽略除函数定义之外的所有内容;他们无法生成 ( ){ } 部分。

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

When faced with the tokens S() { } at namespace scope, the compiler can't magically decide it's a ctor. Which grammar rule would produce such a sequence of tokens? Let's ignore everything but function-definitions; they can't produce the ( ){ } part.

That means that S() must be a declarator , and the decl-specifier-seqopt has to be empty (see §8.4.1). §9.2/7 subsequently tells us that the declarator must name a constructor, destructor, or conversion function. But S doesn't name either. Therefore, S() { } is invalid.

尬尬 2024-12-07 04:20:13

尽管这个问题在最初发布的 C++11 标准中充其量是不明确的,并且在提出这个问题时,缺陷解决方案 1435 于 2013 年 4 月被接受,更改了相关文本,以便在命名空间范围内定义构造函数确实需要一个qualified-id(使用::的语法)作为“名称”。

C++14 中的同一段落(或接近那个时间)是:

构造函数没有名称。构造函数的声明使用以下形式的函数声明符 ([dcl.fct])

    ptr-declarator ( 参数声明子句 ) 异常规范opt 属性说明符序列opt

其中ptr-declarator仅由id-expression、可选的attribute-specifier-seq和可选的括号组成, id-expression 具有以下形式之一:

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

本段还有一些细微的调整,但没有改变这个问题的答案。 当前版本内容如下:

如果声明符是以下形式的函数声明符 ([dcl.fct]),则声明符声明一个构造函数

    ptr-declarator ( 参数声明子句 ) noexcept-specifieropt 属性说明符序列opt

其中ptr-declarator仅由id-expression、可选的attribute-specifier-seq和可选的括号组成, id-expression 具有以下形式之一:

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

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

Although this question was ambiguous at best in the originally published C++11 Standard and at the time of this question, Defect Resolution 1435 was accepted in April 2013, changing the relevant text so that a constructor definition at namespace scope does require a qualified-id (the syntax using ::) as the "name".

The same paragraph in C++14 (or near that time) was:

Constructors do not have names. A declaration of a constructor uses a function declarator ([dcl.fct]) of the form

     ptr-declarator ( parameter-declaration-clause ) exception-specificationopt attribute-specifier-seqopt

where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:

  • in a member-declaration that belongs to the member-specification of a class but is not a friend declaration ([class.friend]), the id-expression is the injected-class-name ([class]) of the immediately-enclosing class;
  • in a member-declaration that belongs to the member-specification of a class template but is not a friend declaration, the id-expression is a class-name that names the current instantiation ([temp.dep.type]) of the immediately-enclosing class template; or
  • in a declaration at namespace scope or in a friend declaration, the id-expression is a qualified-id that names a constructor ([class.qual]).

This paragraph has had a few more minor tweaks, none altering the answer to this question. The current version reads:

A declarator declares a constructor if it is a function declarator ([dcl.fct]) of the form

     ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt

where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:

  • in a friend declaration ([class.friend]), the id-expression is a qualified-id that names a constructor ([class.qual]);
  • otherwise, in a member-declaration that belongs to the member-specification of a class or class template, the id-expression is the injected-class-name ([class.pre]) of the immediately-enclosing entity;
  • otherwise, the id-expression is a qualified-id whose unqualified-id is the injected-class-name of its lookup context.

Constructors do not have names. ...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文