Reputation: 1
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).
Upvotes: 4
Views: 543
Reputation: 1
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 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 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
One can also refer to:
Why destructor cannot be template?
Error: Out-of-line constructor cannot have template arguments C++.
Upvotes: 4
Reputation: 171263
I don't think it's true that this was invalid in C++17.
CWG 1435 introduced the wording that allowed the code (that wording can still be seen in the context for the [class.dtor] changes in CWG 2337):
in a declaration at namespace scope or in a friend declaration, the id-expression is nested-name-specifier ~class-name and the class-name names the same class as the nested-name-specifier."
This allows the code, because C<T>::~C<T>
is an id-expression of that form. The class-name in the destructor ~C<T>
names the same class as in C<T>::
. That wording was present in C++17 and C++20.
P1787R6 Declarations and where to find them changed that wording to:
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."
This no longer allows ~C<T>
because the injected-class-name is just C
.
This seems like a breaking change that was not obvious from the revision history of the r5 paper, which includes:
All compilers (GCC, Clang, EDG and MSVC) accept the program in C++17 mode, and I don't think it's at all clear that the code was always invalid in older standards, as OP claims. It seems to be a possibly-unintentional change in C++23. Edit: I'm reliably informed by Jason Merrill that it was an intentional change for C++23, but not a DR for C++17. See his comment on the other answer.
Upvotes: 2