7245


以下のコードはコンパイルし，動作します．(Wandboxのg++ 4.9.2,clang 3.5.0にて確認，以下同じ)

[code]

    #include <new>
    int main(){
        auto p = new int const(0);
        delete p;
    }
    
[/code]

しかし，delete p;を::operator
delete(p);で置き換えた場合にはコンパイルエラーが発生してしまいます．（g++での例，clangでもコンパイルエラー）

[code]

    prog.cc: In function 'int main()': 
    prog.cc:4:24: error: invalid conversion from 'const void*' to 'void*' [-fpermissive] 
        ::operator delete(p);
    
[/code]

確かにconstなオブジェクトへのポインタは非constなオブジェクトへのポインタには変換できませんし，operator
deleteが非constなオブジェクトへのポインタを要求するのは理解できるのですが，ではなぜ最初のdelete式がコンパイル可能であるのかが分かりません．

N3797の§5.3.5.7より，

> Otherwise, the delete-expression will not call a deallocation function
> (3.7.4.2).

とあるので，`operator delete`の呼び出しが省略されたのかと考えましたが，以下のコードを実行してみたところ，`op
delete`が出力されました．（bは無関係なオブジェクトでの呼び出しを避けるために使用しています）

[code]

    #include <new>
    #include <iostream>
    
    bool b = false;
    void operator delete(void*) noexcept{
        if(b){
            std::cout << "op delete" << std::endl;
        }
    }
    
    int main(){
        b = true;
        auto p = new int const(0);
        delete p;
        b = false;
    }
    
[/code]

この結果はどのように解釈すれば良いのでしょうか．


CC BY-SA 3.0
2015-02-26T15:15:10.817
7158
2015-02-28T07:10:07.533
8573
question
c++
constなオブジェクトへのポインタをdeleteしたときの挙動


まず，`delete-expression` は const ポインタを渡すことができますが，(§5.3.5.2 の Note) `::operator
delete` の直接の呼び出しは delete-expression ではなく，単なる関数呼び出しなので，引数のポインタがそのまま渡され const
ポインタを渡すことができません．  
具体的には，`delete-expression`である delete はデストラクタの呼び出し後にオーバーロードを考慮した operator delete
の呼び出しを行いますが， ::operator delete の直接の呼び出しではデストラクタが呼び出されず，引数のポインタがそのまま渡されます．


CC BY-SA 3.0
2015-02-26T17:39:40.547
7162
2015-02-26T17:39:40.547
8577
7158
answer


ISO/IEC 14882:1998 によれば

5.3.5 delete  
5.3.5-2 Note に const つき型へのポインタを渡してよい constness をキャストで除去する必要は無いとあります。

12.1 constructor, 12.4 destructor にも  
12.1-4 constructor は const, volatile, const volatile オブジェクトに対して呼べる  
constructor が完了するまで const/volatile-ness は適用されない  
12.4-2 destructor は const, volatile, const volatile オブジェクトに対して呼べる  
destructor が開始した時点で const/volatile-ness は適用されなくなる  
とあります。


CC BY-SA 3.0
2015-02-27T05:28:04.847
7195
2015-02-27T05:28:04.847
8586
7158
answer


同じく ISO/IEC 14882:1998 より  
5.3.5 delete  
1 delete-式 は最派生オブジェクトの解体であり  
6 削除されるオブジェクトにデストラクタがあれば呼びだす  
なので

> オブジェクトのdestructionであると解釈すれば

「解釈すれば」は要らないでしょう。

また 12.4 destructors に  
「解体されるオブジェクトの占めている記憶域を解放する」系の文言は一切ありません。

一方で 3.7.3.2 operator delete はその名 [ 解放関数 ] のとおり  
- デストラクタを起動するとは書かれていない  
- 記憶域を解放する (義務がある) と書かれている

一般的実装としては operator delete が直ちに OS 等に直接資源を返すとは考えにくく  
いわゆる free-store (に類する何か) に「未使用」マークを付けるだけ、が多いでしょう。  
なので operator delete に const T* を渡すことには意味が無いと解釈すべきでしょう。  
const だと「未使用」マークを付ける操作すらできないので。

だから一般的 C++ 実装としては  
delete-expression に const/volatile T* が渡されたときの処理系の挙動は  
1. cv 修飾を除去する  
2. デストラクタを呼ぶ (cv 修飾除去後の型で)  
3. 最適解放関数を呼ぶ (cv 修飾除去後の型で)  
となるものと思われます。

蛇足  
pandaman 氏は当然わかっていて故意に書かれているのでしょうが第三者読者のために追記しておくと  
提示サンプルの１  
- new-式 + delete-式の組み合わせ＝正しい  
- new-式 + operator delete の組み合わせ＝デストラクタが呼ばれないので正しくない

提示サンプルの２  
- operator new は処理系提供のもので operator delete は自作＝記憶域は正しく解放されていない  
すなわちメモリリークしている（デストラクタは呼ばれている）

ことだけ注意喚起しておきます。


CC BY-SA 3.0
2015-02-28T07:10:07.533
7245
2015-02-28T07:10:07.533
8589
7158
answer