Деструктор псевдонима typedef
#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.
Ответы
Ответ 1
Почему это относится и к имени деструктора?
Потому что стандарт говорит:
[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 destructor
B_ptr->~B(); // calls D destructor
B_ptr->~B_alias(); // calls D destructor
B_ptr->B_alias::~B(); // calls B destructor
B_ptr->B_alias::~B_alias(); // calls B destructor
}
Дальнейшая спецификация поиска имени, а также пример, который относится к вопросу:
[Basic.lookup.qual]
Если псевдо-деструктор-имя ([expr.pseudo]) содержит спецификатор вложенных имен, имена типов рассматриваются как типы в области, назначенной вложенным именем-спецификатором. Аналогично, в квалифицированном id формы:
nested-name-specifieropt class-name :: ~ class-name
второе имя класса просматривается в той же области, что и первая. [ Пример:
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
}
- конец примера]
Ответ 2
Потому что, когда вы пишете ~AB()
вы не называете или не вызываете деструктор. Вы пишете ~
с последующим именем класса, и вызов деструктора автоматически предоставлен в результате указанной семантики написания этих маркеров рядом друг с другом.
Обычно это академический, но здесь вы видите, почему это может иметь значение.
Точно так же, написав AB()
вы не "вызываете конструктор", хотя это выглядит как вызов функции, и многие новички языка интерпретируют этот код таким образом. (Это может привести к забавному и игровому процессу при попытке вызвать конструктор шаблона без вывода аргумента: не имея возможности назвать конструктор, нет способа предоставить эти аргументы!)
Фактически, ни конструктор, ни деструктор технически даже не имеют имени!
Эти нюансы делают C++ забавой, не так ли?