78367


偶然みつけたものですが、引数オブジェクトのデストラクタを呼ぶテンプレート関数で  
データ型を指定するとなぜかエラーにならず、そのまま処理が通ってしまいますが何故でしょうか？  
また処理が通ってしまうことでバグの原因に繋がるでしょうか？  
Visual Studio2019を使用しています。

[code]

    // main.cpp
    #include <stdio.h>
    
    // コンストラクタ、デストラクタで文字出力するだけのクラス
    class Dummy {
    public:
        Dummy() { printf("Dummy();\n"); }
        virtual ~Dummy() { printf("~Dummy()\n"); }
    };
    
    // デストラクタだけを呼ぶテンプレート関数
    template <typename T>
    void Foo(T* _pPtr)
    {
        _pPtr->~T();
    }
    
    // メイン関数
    int main()
    {
        {
            // 問題なくコンストラクタ、デストラクタが呼ばれる。
            void* _pAlloc = malloc(sizeof(Dummy));
            Dummy* _pObj = new(_pAlloc) Dummy;
            Foo(_pObj);
            free(_pAlloc);
        }
        {
            // データ型を指定すると、エラーにならずに処理が通ってしまう。
            void* _pAlloc = malloc(sizeof(float));
            float* _pObj = new(_pAlloc) float;
            Foo(_pObj);
            free(_pAlloc);
        }
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2021-07-22T10:33:34.487
78360
2021-07-23T10:03:36.137
2021-07-22T14:59:31.903
4236
47396
question
c++
デストラクタを呼ぶテンプレート関数でデータ型を指定すると何故かエラーにならず処理が通ってしまいます。


結論から言うと、 C++ の規格に明記された挙動なので、問題ありません。

C++11 (N3337) の標準規格の [`12.4 Destructors p16`](https://timsong-
cpp.github.io/cppwp/n3337/class.dtor#16) に以下のように書かれています。

> the notation for explicit call of a destructor can be used for any scalar
> type name ([expr.pseudo]). Allowing this makes it possible to write code
> without having to know if a destructor exists for a given type. For example,  
>  参考訳: スカラの型名に対して、デストラクタの明示的な呼出しの記法を使うことができる ([expr.pseudo])
> 。それを許すことによって、型に対するデストラクタがあるか否かを知らなくても、コードを書くことができる。例えば：  
>  For example:
[code]

>     typedef int I;
>     I* p;
>     p->I::~I();
>  
[/code]

参考: <https://stackoverflow.com/questions/456310/destructors-of-builtin-types-
int-char-etc>

この、スカラ型（厳密には、クラスではない型）に対する呼び出しは、「疑似デストラクタ」と呼ばれていて、 その挙動は [`5.2.4 Pseudo
destructor call`](https://timsong-cpp.github.io/cppwp/n3337/expr.pseudo#1)
で定義されています。  
具体的には、 `p` が評価される以外の効果は発生しません。  
また、ドット演算子の左辺はスカラ型、アロー演算子の左辺はスカラ型へのポインタでなけれなりません。

上記の例にあるように、テンプレートを使わずとも、 typedef で組み込み型のキーワードに別名をつければ、明示的に呼び出すことが可能です。  
`_pObj->~float();` とするとエラーになってしまう (※) のは、 float 型がスカラ型であるからではなく、 `float`
が識別子ではない（キーワードである）からでしょうね。  
※: g++ (GNU C++ Compiler) ではエラーになりましたが、コンパイラーによってはエラーにならないかもしれません。

なお、ここで言うスカラ型というのは、 [`3.9 Types p9`](https://timsong-
cpp.github.io/cppwp/n3337/basic.types#9) で定義されている、算術型・列挙型・ポインタ型・メンバへのポインタ型
を指します。  
つまり、（構造体を含む）クラス型やスカラ型以外のもの、例えば配列型などでは、明示的なデストラクタの呼び出しの記述が行えません。

[code]

    #include <iostream>
    typedef int I;
    typedef int A[3];
    
    int main() {
        I i = 1;
        i.~I();
        // ↑ これは可能
    
        A a = { 0, 1, 2 };
        a.~A();
        // ↑ これはエラー
    }
    
[/code]

* * *

ちなみに、 C++03 (N1577) でも、 `12.4 デストラクタ p15` で同様の内容が記載されています。  
C++03 であれば、対応する JIS規格
([JISX3014](https://www.jisc.go.jp/app/jis/general/GnrJISNumberNameSearchList?show&jisStdNo=X3014))
が存在するので、日本語でその内容を確認できます。


CC BY-SA 4.0
2021-07-22T15:52:28.237
78367
2021-07-23T10:03:36.137
2021-07-23T10:03:36.137
8237
8237
78360
answer