Подтвердить что ты не робот

Если разрешено 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
        ]

Ожидается ли это? Я пишу класс, где я хотел иметь уникальный указатель в классе. При попытке выработать семантику конструктора перемещения для класса я столкнулся с этим (я предполагаю, потому что я, наконец, правильно закодировал свой конструктор перемещения, т.е. Другие ошибки были исправлены).

4b9b3361

Ответ 1

MSVC прав, а GCC ошибочен:

Стандарт (3.9/5):

Неполно определенные типы объектов и типы пустот являются неполными типами

Стандарт (20.7.1.1.2/4):

Если T является неполным, программа плохо сформирована

Ответ 2

На самом деле у 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");

Таким образом, используя последний код на внешней линии, ваш пример не скомпилируется, как требуется стандартом.

Ответ 3

Вопрос сводится к следующему:

void* p = new int;
delete p;

Глядя на n3797 5.3.5 Исключить, я считаю, что поведение delete p - undefined из-за несоответствующих типов, поэтому поведение компилятора допустимо, поскольку код является ошибкой.

Примечание: это отличается от shared_ptr<void>, поскольку в нем используется стирание стилей, чтобы отслеживать исходный тип переданного указателя.

Ответ 4

Не удалять переменные void *.

Если вы хотите работать с чем-то вроде Win32 Handles, пожалуйста, укажите пользовательский deleter.

Например:

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

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

Тогда:

UniHandle ptr(..., HandleDeleter);