The standard rules:
A name prefixed by the keyword template shall be followed by a template argument list or refer to a class template or an alias template. The latter case is deprecated ([depr.template.template]). The keyword template shall not appear immediately before a ~ token (as to name a destructor).
This prohibits the use of the template
keyword.
A postfix expression followed by a dot . or an arrow ->, optionally followed by the keyword template, and then followed by an id-expression, is a postfix expression. The postfix expression before the dot or arrow is evaluated;53 the result of that evaluation, together with the id-expression, determines the result of the entire postfix expression.
[expr.prim.id.unqual]/nt:unqualified-id:
unqualified-id: ... ~ type-name ~ decltype-specifier ...
type-name can never be qualified names, so this prohibits the use of it.
So if I want to use std::string
in the destructor, I can only do this:
std::string str;
str.std::string::~string();
new (&str) std::string;
or:
std::string str;
using std::string;
str.~string();
new (&str) std::string;
Why is there such rules?
CodePudding user response:
you can use std::destroy_at
to do it
void foo(){
std::string str;
std::destroy_at(&str);
std::construct_at(&str);
}
CodePudding user response:
The underlying issue here is that std::string
is not a class - it is only an alias for std::basic_string
.
So the type of str
is actually std::basic_string<char>
and not std::string
.
std::string
is not visible during member lookup inside std::basic_string
, because it is not part of the class.
So if you want to use ~string
you would either have to make it visible for unqualified lookup (i.e. via using std::string;
) or prefix it (str.~std::string::~string();
).
The only destructor that member lookup can find is std::basic_string::~basic_string()
, so this would be the one you would need to use:
str.~basic_string();
CodePudding user response:
fwiw, you can also call it in other ways
void foo(){
std::string str;
str.~basic_string();
new (&str) std::string;
}
void foo_explicit_type(){
std::string str;
str.~basic_string<char>();
new (&str) std::string;
}
void foo_alias(){
using X = std::string;
std::string str;
str.~X();
new (&str) std::string;
}
The relevant text is (also) at [class.dtor]
In an explicit destructor call, the destructor is specified by a
~
followed by atype-name
ordecltype-specifier
that denotes the destructor's class type.[Example 1:
struct B { virtual ~B() { } }; struct D : B { ~D() { } }; D D_object; typedef B B_alias; B* B_ptr = &D_object; void f() { D_object.B::~B(); // calls B's destructor B_ptr->~B(); // calls D's destructor B_ptr->~B_alias(); // calls D's destructor B_ptr->B_alias::~B(); // calls B's destructor B_ptr->B_alias::~B_alias(); // calls B's destructor }
— end example]