web-dev-qa-db-ja.com

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!

最初の2行はユーザーのデストラクタ呼び出しに属し、3行目はmain関数のスコープを終了するときに呼び出されるデストラクタによるものと想定しています。

私の理解では、typedefは型のエイリアスです。この場合、ABAのエイリアスです。

なぜこれがデストラクタの名前にも当てはまるのですか?言語仕様への参照は非常に高く評価されています。

編集:これは、macOS HighSierraバージョン10.13.3でApple LLVMバージョン9.1.0(clang-902.0.39.1)を使用してコンパイルされました。

22

なぜこれがデストラクタの名前にも当てはまるのですか?

標準が言うので:

[class.dtor]

明示的なデストラクタ呼び出しでは、デストラクタは〜で指定され、その後にデストラクタのクラスタイプを示すtype-nameまたはdecltype-specifierが続きます。 .。

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])にネストされた名前指定子が含まれている場合、タイプ名はネストされた名前指定子によって指定されたスコープ内のタイプとして検索されます。同様に、次の形式の修飾IDで:

ネストされた名前-specifieroptクラス名::〜クラス名

2番目のクラス名は、最初のクラス名と同じスコープで検索されます。 [例:

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
}

—例を終了]

21
eerorika

~AB()を書くときは、デストラクタに名前を付けたり呼び出したりしていないからです。あなたは_~_に続いて インクルード クラスの名前、およびデストラクタ呼び出しは、これらのトークンを隣り合わせに書き込むという指定されたセマンティクスの結果として自動的にプロビジョニングされます。

通常、これは学術的なものですが、ここでそれが重要になる理由がわかります。

同様に、AB()を記述することにより、これは関数呼び出しのように見え、多くの言語の初心者がコードをこのように解釈しますが、「コンストラクターを呼び出す」ことにはなりません。 (これは、引数を差し引いてテンプレートコンストラクターを呼び出そうとすると、面白くてゲームにつながる可能性があります。コンストラクターに名前を付けることができなければ、それらの引数を提供する方法はありません!)

実際、コンストラクタもデストラクタも技術的にはhave名前でさえありません!

これらのニュアンスはC++を楽しくしますよね?