Home > other >  Why does the standard disallow an explicit destructor call with ~ preceded by the template keyword a
Why does the standard disallow an explicit destructor call with ~ preceded by the template keyword a

Time:08-25

The standard rules:

[temp.names]/6

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.

[expr.ref]/1

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 a type-name or decltype-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]

  • Related