12

The C++ program (somewhat unexpectedly, at first, to me) compiles and runs fine, except for the line commented at the end of main(), which is a compile-time error if it is uncommented.

#include <typeinfo>
#include <iostream>

struct Foo {
    int x;
};

template <typename T>
void create(char *buffer)
{
    std::cout << "creating " << typeid(T).name() << std::endl;
    new (buffer) T();
}

template <typename T>
void destroy(char *buffer)
{
    std::cout << "destroying " << typeid(T).name() << std::endl;
    ((T*)buffer)->~T();
}

int main(int argc, char **argv)
{
    char buffer[sizeof(Foo) > sizeof(bool) ? sizeof(Foo) : sizeof(bool)];

    // create/destroy Foo via template function calls
    create<Foo>(buffer);
    destroy<Foo>(buffer);
    // now do the above explicitly...
    new (buffer) Foo();
    ((Foo*)buffer)->~Foo();

    // create/destroy bool via template function calls
    create<bool>(buffer);
    destroy<bool>(buffer);
    // now do the above explicitly...
    new (buffer) bool();
    // ((bool*)buffer)->~bool(); // oops, doesn't work

    return 0;
}

I gather from this that C++ (or at least g++'s idea of C++) allows an "explicit destructor call" of a template parameter type, even when manually doing the type replacement oneself results in a syntax error (since bool doesn't really actually have a destructor to call).

To be more explicit, the line:

((T*)buffer)->~T();

compiles and runs fine when T is instatiated on a bool, but doing the same thing with an actual bool:

((bool*)buffer)->~bool();

is a syntax error.

I'm discovered this behavior as I was doing template metaprogramming and find it a very helpful, so I'm guessing it's standard and was added specifically to handle cases like the one I have above. Does anyone know for sure if this is actually the case, and when this behavior was standardized if it is?

In addition, can anyone point to what the exact wording in the standard is that allows this, and the scope of the situations it allows? (I'm not adept at reading standardese so I have a hard time figuring this out myself.)

Trantorian
  • 349
  • 1
  • 12
  • This is similar to [this](http://stackoverflow.com/questions/16720201/calling-destructor-explicitly) question. But I'm unsure if it is an exact duplicate. – martin May 13 '15 at 06:08
  • @martin I'm asking specifically about the case where the template is instantiated on a builtin type, which I don't think the question covers actually... – Trantorian May 13 '15 at 06:09
  • When compiled in Visual Studio, I get "error C2059: syntax error : 'type' – irsis May 13 '15 at 06:28

1 Answers1

12

This is indeed valid C++ (and has been since C++98, as far as I know), and known as a pseudo-destructor call. N4431 §5.2.4 [expr.pseudo]:

1 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.

2 The left-hand side of the dot operator shall be of scalar type. The left-hand side of the arrow operator shall be of pointer to scalar type. This scalar type is the object type. The cv-unqualified versions of the object type and of the type designated by the pseudo-destructor-name shall be the same type. Furthermore, the two type-names in a pseudo-destructor-name of the form

nested-name-specifier_opt type-name :: ~ type-name

shall designate the same scalar type.

A pseudo-destructor-name is one of (§5.2 [expr.post]):

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

While a type-name is one of (§7.1.6.2 [dcl.type.simple])

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

bool is not a type-name, so the ~bool() version is a syntax error. Inside a template, a template type parameter is a typedef-name (§14.1 [temp.param]/p3), which is a type-name, so the ~T() version compiles.

T.C.
  • 133,968
  • 17
  • 288
  • 421
  • I've a question If according to above standard if it is valid C++ then why doesn't it compile ? – irsis May 13 '15 at 06:29
  • @T.C. Thanks, accepted. Just curious, what does standardese consider `bool` to be, if not a _type-name_? – Trantorian May 13 '15 at 06:32
  • @Trantorian `bool` is a *simple-type-specifier*. – T.C. May 13 '15 at 06:33
  • @T.C. Hah, ok, thanks. Funny how that's not a subset of _type-name_, but that's standardese for you, I guess. – Trantorian May 13 '15 at 06:34
  • @T.C. is this a small bug in the language or deliberately, since destructors on *simple-type-specifiers* need not to be called explicitely? – martin May 13 '15 at 06:34
  • 1
    @Trantorian To the contrary, *type-name* is a subset of *simple-type-specifier*s :) – T.C. May 13 '15 at 06:35
  • 3
    @martin (Assuming that you mean scalar types) my guess is that it's deliberate; no point in letting people write completely useless code. – T.C. May 13 '15 at 06:39
  • @T.C., would you mind adding the section number of `dcl.type.simple` to your answer? I find navigating the standard easier with section numbers. – R Sahu May 13 '15 at 06:42