Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clang accepts out of class destructor definition while gcc does not

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
MSVC C++17 Yes
MSVC C++20 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).

like image 220
Anoop Rana Avatar asked Oct 12 '25 05:10

Anoop Rana


1 Answers

The program is ill-formed atleast starting from c++20 and clang and msvc are wrong in accepting the code with c++20 and onwards.

Note that the change in wording for class.dtor was introduced in C++23 via p1787r6-class.dtor and seems to be a DR for C++20.

So, the code is ill-formed from C++20 and onwards which 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 clang bug report:

Clang accepts invalid out of class definition for destructor

Here is msvc bug report:

MSVC accepts invalid out of class definition for a destructor of a class template with c++20


For further reading

One can also refer to:

Why destructor cannot be template?

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

template<typename T> someclass<T>::someclass<T>() is not allowed when providing an out of class definition for a constructor and a destructor with any c++ version.

like image 159
Anoop Rana Avatar answered Oct 14 '25 19:10

Anoop Rana