Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ std::destroy(T * pointer)

Tags:

c++

stl

The STL code I am reading may be old...but the question is more related to C++ template grammar.

The question surrounds this stl template function:

template<class T> std::destroy(T *p) {
    p->~T();
}

I can't seem to find a specialization of the std::destroy(T *) function. So it seems to me that the template function will instantiate same for "int" types, and invoke "int"'s destructor. To make my point, I created this sample code that emulate the std::destroy. I call it my_destroy ih the example.

#include <iostream>
#include <stdio.h>
using namespace std;

template <class T> 
void my_destroy(T * pointer) {
    pointer->~T(); 
}
int main()
{
    int *a;
    //a->~int();        // !!! This won't compile.
    my_destroy<int>(a); // !!! This compiles and runs.
}

}

To my surprise, this line doesn't compile:

a->~int();

But this line compiles:

my_destroy<int>(a);

My confusion is, I thought that my_destroy<int>(a) will be instantiated as the equivalent of a->~int();

To a question in larger context, when a STL container of <int> erases an element, how does the std::destroy() work?

like image 932
ROTOGG Avatar asked Jul 19 '13 21:07

ROTOGG


3 Answers

Note that, while a->~int(); doesn't compile, this does:

typedef int INT;
int* a;
a->~INT();

From the standard:

5.2.4p1 The use of a pseudo-destructor-name after a dot . or arrow -> operator represents the destructor for the non-class type denoted by type-name or decltype-specifier. The result shall only be used as the operand for the function call operator (), and the result of such a call has type void. The only effect is the evaluation of the postfix-expression before the dot or arrow.

From 5.2p1:

pseudo-destructor-name:
  nested-name-specifier_opt type-name :: ~ type-name
  nested-name-specifier template simple-template-id :: ~ type-name
  nested-name-specifier_opt~ type-name
  ~ decltype-specifier

And finally, 7.1.6.2p1:

type-name:
  class-name
  enum-name
  typedef-name
  simple-template-id

So, curiously, int is not syntactically a type-name (it's a simple-type-specifier) and so you can't call ~int(), but INT is and so you can.

like image 114
Igor Tandetnik Avatar answered Nov 15 '22 19:11

Igor Tandetnik


For class types (non-scalar types), the expression

pointer->~T();

is essentially a function call (a postfix-expression). To identify the function to be called, the part pointer->~T is must be analysed. The ~T is an id-expression IFF T is a class-name, identifying the destructor function.

Of course, int is not a class name. But if T is a type-name naming a scalar type, the same expression is parsed differently. The whole part pointer->~T is identified as a special postfix-expression called a pseudo-destructor name. With the following () the expression is considered a call of a pseudo-destructor (the rules in [expr.pseudo] forbid to do anything else with a pseudo-destructor name but to call it).

int itself is not a type-name [dcl.type.simple], but a simple-type-specifier:

type-name:

  • class-name
  • enum-name
  • typedef-name
  • simple-template-id

That's why you can use a typedef'd int or T as in your example (*), but not int directly. The reasoning has been well explained by sehe.

The rules for pseudo-destructor calls are specified in [expr.pseudo]: "The only effect is the evaluation of the postfix-expression before the dot or arrow."

(*) from [temp.param]/3: "A type-parameter whose identifier does not follow an ellipsis defines its identifier to be a typedef-name [...]"

like image 34
dyp Avatar answered Nov 15 '22 18:11

dyp


The language allows these things to enable generic programming. However, your 'straight' invocation is not in generic code, hence it fails.

Another such case is

template <typename T> void foo()
{
    T instance = T(); // default constructor
}

foo<int>(); // will work

So, yes:

template <typename T> void foo()
{
    T* instance = new T(); // default constructor
    delete instance;      
}

will also work for builtin primitive types, because T is a typename in the scope ot the template.

like image 3
sehe Avatar answered Nov 15 '22 19:11

sehe