Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destructor of typedef alias

#include <iostream>

struct A { ~A(); };
A::~A() {
    std::cout << "Destructor was called!" << std::endl;
}

typedef A AB;
int main() {
    AB x;
    x.AB::~AB(); // Why does this work?
    x.AB::~A();
}

The output of the above program is:

Destructor was called!
Destructor was called!
Destructor was called!

I assume the first two lines belonging to user destructor calls, while the third line is due to the destructor being called when exiting the scope of main function.

From my understanding a typedef is an alias for a type. In this case AB is an alias for A.

Why does this apply for the name of the destructor too? A reference to the language specification is very much appreciated.

Edit: This was compiled using Apple LLVM version 9.1.0 (clang-902.0.39.1) on macOS High Sierra Version 10.13.3.

like image 602
Felix Crazzolara Avatar asked Sep 10 '18 11:09

Felix Crazzolara


People also ask

What happens if you don't declare a destructor in C++?

If a base class A or a member of A has a destructor, and a class derived from A does not declare a destructor, a default destructor is generated. The default destructor calls the destructors of the base class and members of the derived class.

What is destructor explain with example?

A destructor is a member function that is invoked automatically when the object goes out of scope or is explicitly destroyed by a call to delete . A destructor has the same name as the class, preceded by a tilde ( ~ ). For example, the destructor for class String is declared: ~String() .

What is the rule of destructor in classes?

If the destructor of the base class is protected, you can not destroy derived objects using a base class pointer; therefore, the destructor must not be virtual. Only to make the point clear about types (not pointers or references): If the destructor of a class Base is private, you can not use the type.

What is the use of destructor in C++?

Destructors are usually used to deallocate memory and do other cleanup for a class object and its class members when the object is destroyed. A destructor is called for a class object when that object passes out of scope or is explicitly deleted.

What are aliases and typedefs in C++?

Aliases and typedefs (C++) You can use an alias declaration to declare a name to use as a synonym for a previously declared type. (This mechanism is also referred to informally as a type alias ). You can also use this mechanism to create an alias template, which can be particularly useful for custom allocators.

What is explicit call of destructor in C++?

The notation for explicit call of a destructor can be used for any scalar type name. Allowing this makes it possible to write code without having to know if a destructor exists for a given type.

What comes after ~ in a destructor call?

The rule is, that in a destructor call, the thing after the ~ is a type-name. int is not such a thing, but a typedef-name is. Look it up in 7.1.5.2.

Is it possible to declare a nameless structure in a typedef?

Although the C practice of declaring a nameless structure in a typedef statement still works, it provides no notational benefits as it does in C. The preceding example declares a class named POINT using the unnamed class typedef syntax. POINT is treated as a class name; however, the following restrictions apply to names introduced this way:


Video Answer


2 Answers

Why does this apply for the name of the destructor too?

Because standard says:

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

A typedef alias is a type-name which denotes the same class as the type-name of the class itself.

The rule even has a clarifying example:

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
}

Further specification about the name lookup, also with an example that applies to the question:

[basic.lookup.qual]

If a pseudo-destructor-name ([expr.pseudo]) contains a nested-name-specifier, the type-names are looked up as types in the scope designated by the nested-name-specifier. Similarly, in a qualified-id of the form:

nested-name-specifieropt class-name :: ~ class-name

the second class-name is looked up in the same scope as the first. [ Example:

struct C {
  typedef int I;
};
typedef int I1, I2;
extern int* p;
extern int* q;
p->C::I::~I();      // I is looked up in the scope of C
q->I1::~I2();       // I2 is looked up in the scope of the postfix-expression

struct A {
  ~A();
};
typedef A AB;
int main() {
  AB* p;
  p->AB::~AB();     // explicitly calls the destructor for A
}

— end example  ]

like image 100
eerorika Avatar answered Oct 12 '22 12:10

eerorika


Because when you write ~AB() you are not naming or calling the destructor. You are writing ~ followed by the a name of the class, and the destructor call is automatically provisioned as a result of the specified semantics of writing those tokens next to each other.

Usually this is academic but here you see why it can matter.

Similarly, by writing AB() you are not "calling a constructor", even though this looks like a function call and many language newcomers interpret the code this way. (This can lead to fun and games when attempting to call a template constructor without argument deduction: without being able to name the constructor, there's no way to provide those arguments!)

In fact, neither the constructor nor the destructor technically even have a name!

These nuances make C++ fun, right?

like image 21
Lightness Races in Orbit Avatar answered Oct 12 '22 12:10

Lightness Races in Orbit