これは非常に簡単な質問です。次のコードについて考えてみます。
#include <iostream>
#include <memory>
typedef std::unique_ptr<void> UniqueVoidPtr;
int main() {
UniqueVoidPtr p(new int);
return 0;
}
次のコマンドを使用してcygwin(g ++ 4.5.3)でコンパイルするg++ -std=c++0x -o prog file.cpp
は問題なく動作します。ただし、Microsoftコンパイラ(VS 2010または2013)でコンパイルすると、次のエラーが発生します。
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\memory(2067) : error C2070: 'void': illegal sizeof operand
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\memory(2066) : while compiling class template member function 'void std::default_delete<_Ty>::operator ()(_Ty *) const'
with
[
_Ty=void
]
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\type_traits(650) : see reference to class template instantiation 'std::default_delete<_Ty>' being compiled
with
[
_Ty=void
]
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\memory(2193) : see reference to class template instantiation 'std::tr1::is_empty<_Ty>' being compiled
with
[
_Ty=std::default_delete<void>
]
foo1.cpp(7) : see reference to class template instantiation 'std::unique_ptr<_Ty>' being compiled
with
[
_Ty=void
]
これは予想されますか?クラス内に一意のポインタを置きたいクラスを書いています。クラスのmoveコンストラクターのセマンティクスを理解しようとしているときに、これに遭遇しました(moveコンストラクターが最終的に正しくコーディングされたためだと思います:つまり、他のエラーが修正されました)。
MSVCは正しいが、GCCは間違っている:
標準(3.9/5):
不完全に定義されたオブジェクトタイプとvoidタイプは不完全なタイプです
標準(20.7.1.1.2/4):
Tが不完全な型の場合、プログラムの形式が正しくありません。
GCCには実際にはそれを防ぐためのコードがありますが、最近まで機能しませんでした。
GCCのunique_ptr
には、default_deleter::operator()
に静的アサーションがあり、不完全な型を拒否する必要があります。
static_assert(sizeof(_Tp)>0,
"can't delete pointer to incomplete type");
ただし、拡張機能としてGCCは sizeof(void)
をサポートしているため、アサーションは失敗せず、システムヘッダーに表示されるため、警告も表示されません(-Wsystem-headers
を使用しない限り) )。
私は最近この問題を自分で発見したので、それを修正するためにこれを追加しました 10日前 :
static_assert(!is_void<_Tp>::value,
"can't delete pointer to incomplete type");
そのため、トランクで最新のコードを使用すると、標準で要求されているように、サンプルのコンパイルに失敗します。
質問は要約すると:
void* p = new int;
delete p;
N3797 5.3.5 Deleteを見ると、タイプが一致しないため、delete p
は未定義の動作であると思われます。したがって、コードにバグがあるため、どちらのコンパイラの動作も許容されます。
注:これはshared_ptr<void>
とは異なります。これは、型消去を使用して、渡された元の型のポインターを追跡するためです。
void *
の変数は削除しないでください。
Win32ハンドルのようなもので作業したい場合は、カスタム削除機能を提供してください。
例えば:
void HandleDeleter(HANDLE h)
{
if (h) CloseHandle(h);
}
using UniHandle = unique_ptr<void, function<void(HANDLE)>>;
次に:
UniHandle ptr(..., HandleDeleter);