#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.
Обратите внимание, что деструктор формально не имеет имени (как и конструкторы).
@molbdnilo Я написал этот пример некоторое время назад. Если я правильно помню, это было вдохновлено статьей о cppreference, поэтому я предполагаю, что они, в свою очередь, взяли пример из языкового стандарта.
Кстати, вы вызываете UB, несколько раз уничтожая один и тот же объект.





Why does this apply for the name of the destructor too?
Потому что стандарт гласит:
[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. ...
Псевдоним 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]
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 ]
... и имя ищется в глобальной области вместо области A в [basic.lookup.qual] / 6.
Потому что, когда вы пишете ~AB(), вы не называете и не вызываете деструктор. Вы пишете ~, за которым следует в - имя класса, и вызов деструктора автоматически предоставляется в результате указанной семантики записи этих токенов рядом друг с другом.
Обычно это академический характер, но здесь вы понимаете, почему это может иметь значение.
Точно так же, записывая AB(), вы не «вызываете конструктор», даже если это выглядит как вызов функции, и многие новички в языке интерпретируют код именно так. (Это может привести к забавам и играм при попытке вызвать конструктор шаблона без вывода аргументов: не имея возможности назвать конструктор, невозможно предоставить эти аргументы!)
Фактически, ни конструктор, ни деструктор технически даже не имеют имя!
Эти нюансы делают C++ интересным, не так ли?
(Более "забавный" ответ, дополняющий запрашиваемое жирное стандартное письмо)
По этому показателю C++ имеет на порядок больше удовольствия на бит, чем любой другой популярный компьютерный язык.
@Eljay И поэтому нам это нравится ^ _ ^
Я думаю, что это не только более «забавный», но также очень полезный и информативный ответ.
@molbdnilo Ну, я не люблю хвастаться;) (да, люблю)
Этот код очень похож на пример из §3.4.3 в C++ 11.