Home > Back-end >  Clang accepts out of class destructor definition while gcc does not
Clang accepts out of class destructor definition while gcc does not

Time:10-03

I was writing an out-of-class destructor definition for a class template when I noticed that the program compiles with clang with c 17 and c 20 and also with gcc with c 17 but rejected with gcc c 20. Demo.

template<typename T>
struct C
{
    ~C(); 
};
template<typename T>
C<T>::~C<T>()           //accepted by compilers
{
    
}
int main()
{
    C<int> c;;
}

The result of the above program is summarized in the below table:

Compiler C Version Accepts-Code
GCC C 17 Yes
GCC C 20 No
GCC C 2b No
Clang C 17 Yes
Clang C 20 Yes
Clang C 2b Yes

As we can see in the above both of the compilers accept the code except that gcc with c 20 and onwards reject it with the error error: template-id not allowed for destructor.

So, my question is which compiler is right here(if any).

CodePudding user response:

The program is ill-formed and the respective compilers are wrong in accepting the code.

This can be seen from class.dtor#1.2 which states that:

1 A declaration whose declarator-id has an unqualified-id that begins with a ~ declares a prospective destructor; its declarator shall be 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:

1.2 otherwise, the id-expression is nested-name-specifier ~class-name and the class-name is the injected-class-name of the class nominated by the nested-name-specifier.

(emphasis mine)

And since the class-name is the injected-class-name C and not C<T> in our example, the correct way to write an out of class implementation for the destructor would be as shown below:

template<typename T>
struct C
{
    ~C(); //this is an ordinary destructor(meaning it is not templated)
};
template<typename T>
//-----v----------------> C is the injected-class-name and not C<T>
C<T>::~C()
{
    
}
int main()
{
    C<int> c;;
}

Demo

Here is the gcc bug report:

GCC accepts invalid out of class definition for destructor with C 17

Here is the clang bug report:

Clang accepts invalid out of class definition for destructor

For further reading

One can also refer to

Why destructor cannot be template?

Error: Out-of-line constructor cannot have template arguments C .

CodePudding user response:

TL;DR

This is ill-formed from C 20 forward, so gcc is correct here.

Details

This is defect report 2237 which says, amongst other things:

The term class-name includes simple-template-id. It is not clear that allowing a constructor declaration of the form

template<class T> struct X {
X<T>(T); // constructor    }; is useful or helpful.

and the resolution added the following in the compatibility section [cpp.diff.class]p2:

Affected subclauses: 11.4.5 [class.ctor], 11.4.7 [class.dtor] Change: A simple-template-id is no longer valid as the declarator-id of a constructor or destructor. Rationale: Remove potentially error-prone option for redundancy. Effect on original feature: Valid C 2017 code may fail to compile.

template<class T>   struct A {
  A<T>();  // error: simple-template-id not allowed for constructor
  A(int);  // OK, injected-class-name used
  ~A<T>(); // error: simple-template-id not allowed for destructor   
};

and specifically noted that:

(Note that this resolution is a change for C 20, NOT a defect report against C 17 and earlier versions.)

  • Related