Strange construct & destructor syntax in GCC & clang (void* return type)

Viewed 200

While writing some C++ code (compiling with clang, x86_64 linux) I accidentally wrote the following construct:

class Class {
    *Class() {}
};

i.e. with a asterisk (*) in front of the constructor name.

Trying a bit more I noticed you can put an arbitrary amount of * in front; and it also works for destructors, i.e.,

class Class {
    ********Class() {}
    ********~Class() {}
};

Clang compiles it without any errors or warnings.

GCC however gives the warning

control reaches end of non-void function

which leads me to believe that I am actually declaring a constructor/destructor with return type void* (or void********). And writing any kind of return statement with a value gives an error (as expected):

return nullptr;
return {};
...

Interestingly the generated LLVM IR bitcode correctly contains a void function:

define void @_ZN5ClassC2Ev(%struct.Class* %this) {...}
define void @_ZN5ClassD2Ev(%struct.Class* %this) {...}

Searching for any information on this didn't turn up any results. So my question: Is this standard conform C++ or a bug in the compiler frontends of GCC & Clang? Or maybe some compatibility feature? If correct what is the use case for it.

1 Answers

It looks like a bug. Because it's not standard conforming. The standard way to declare or define a constructor is in [class.ctor]/1:

Constructors do not have names. In a declaration of a constructor, the declarator is a function declarator 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 member-declaration that belongs to the member-specification of a class but is not a friend declaration, the id-expression is the injected-class-name 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 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]).

The class-name shall not be a typedef-name. In a constructor declaration, each decl-specifier in the optional decl-specifier-seq shall be friend, inline, explicit, or constexpr.

As you can see in the part I emphasized, the ptr-declarator has to be an id-expression only. Or in simpler and slightly less accurate terms, it must be just the class name.

So why can you add an asterisk in Clang and GCC? It would seem they have a bug, and don't apply the semantic constraints on the ptr-declarator here. Those constraints are important, because the pure grammar does allow an asterisk. It is in [dcl.decl]/4 (only partly copied):

Declarators have the syntax

ptr-declarator:
    noptr-declarator
    ptr-operator ptr-declarator

ptr-operator:
    * attribute-specifier-seqopt cv-qualifier-seqopt
    & attribute-specifier-seqopt
    && attribute-specifier-seqopt
    nested-name-specifier * attribute-specifier-seqopt cv-qualifier-seqopt

See that ptr-operator bit of the syntax? It's that which says a * can appear where you put it. Or a & and even &&. But the semantics placed on the declaration, the paragraph I quoted first, makes it illegal.

So one must conclude it's a bug in GCC and Clang.

Related