web-dev-qa-db-ja.com

std :: unique_ptr <void>を許可する必要があります

これは非常に簡単な質問です。次のコードについて考えてみます。

#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コンストラクターが最終的に正しくコーディングされたためだと思います:つまり、他のエラーが修正されました)。

24
Andrew Falanga

MSVCは正しいが、GCCは間違っている:

標準(3.9/5):

不完全に定義されたオブジェクトタイプとvoidタイプは不完全なタイプです

標準(20.7.1.1.2/4):

Tが不完全な型の場合、プログラムの形式が正しくありません。

21
ixSci

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");

そのため、トランクで最新のコードを使用すると、標準で要求されているように、サンプルのコンパイルに失敗します。

22
Jonathan Wakely

質問は要約すると:

void* p = new int;
delete p;

N3797 5.3.5 Deleteを見ると、タイプが一致しないため、delete pは未定義の動作であると思われます。したがって、コードにバグがあるため、どちらのコンパイラの動作も許容されます。

注:これはshared_ptr<void>とは異なります。これは、型消去を使用して、渡された元の型のポインターを追跡するためです。

7
Nevin

void *の変数は削除しないでください。

Win32ハンドルのようなもので作業したい場合は、カスタム削除機能を提供してください。

例えば:

void HandleDeleter(HANDLE h)
{
    if (h) CloseHandle(h);
}

using UniHandle = unique_ptr<void, function<void(HANDLE)>>;

次に:

UniHandle ptr(..., HandleDeleter);
3
Wizard Z.