Деструктор псевдонима типаdef - PullRequest
0 голосов
/ 10 сентября 2018
#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();
}

Вывод вышеуказанной программы:

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

Я предполагаю, что первые две строки принадлежат пользовательским вызовам деструктора, тогда как третья строка связана с вызовом деструктора при выходе из области действия main функции.

Насколько я понимаю, typedef - это псевдоним для типа. В этом случае AB является псевдонимом для A.

Почему это относится и к имени деструктора? Ссылка на спецификацию языка очень ценится.

Редактировать: это было скомпилировано с использованием Apple LLVM версии 9.1.0 (clang-902.0.39.1) в macOS High Sierra версии 10.13.3.

Ответы [ 2 ]

0 голосов
/ 10 сентября 2018

Потому что когда вы пишете ~AB(), вы не называете и не вызываете деструктор. Вы пишете ~, за которым следует имя класса, и вызов деструктора автоматически предоставляется в результате указанной семантики записи этих токенов рядом друг с другом.

Обычно это академично, но здесь вы понимаете, почему это может иметь значение.

Аналогично, при написании AB() вы не «вызываете конструктор», даже если это похоже на вызов функции, и многие новички языка интерпретируют код таким образом. (Это может привести к веселью и играм при попытке вызвать конструктор шаблона без вывода аргументов: без возможности назвать конструктор, эти аргументы не могут быть предоставлены!)

Фактически, ни конструктор, ни деструктор технически даже не имеют имя!

Эти нюансы делают C ++ забавным, верно?

0 голосов
/ 10 сентября 2018

Почему это относится и к имени деструктора?

Потому что стандарт гласит:

[class.dtor]

При явном вызове деструктора деструктор задается символом ~, за которым следует имя типа или спецификатор decltype. это обозначает тип класса деструктора. ...

Псевдоним typedef - это имя типа, которое обозначает тот же класс, что и имя типа самого класса.

В правиле даже есть поясняющий пример:

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
}

Дополнительные сведения о поиске имени, также с примером, применимым к вопросу:

[basic.lookup.qual]

Если псевдодеструктор-имя ([expr.pseudo]) содержит nested-name-спецификатор, имена типов ищутся как типы в область, обозначенная спецификатором вложенного имени. Точно так же в уточненный идентификатор формы:

nested-name-specieropt имя-класса :: ~ имя-класса

имя второго класса ищется в той же области видимости, что и первое. [Пример:

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
}

- конец примера]

...