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 anid-expression
, an optional attribute-specifier-seq, and optional surrounding parentheses, and theid-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;;
}
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.)