开发者

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

开发者 https://www.devze.com 2023-03-29 11:20 出处:网络
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.

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.

开发者_Python百科

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?


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.


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.


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. ...

0

精彩评论

暂无评论...
验证码 换一张
取 消