Ответ 1
MSVC прав, а GCC ошибочен:
Стандарт (3.9/5):
Неполно определенные типы объектов и типы пустот являются неполными типами
Стандарт (20.7.1.1.2/4):
Если T является неполным, программа плохо сформирована
Это очень простой вопрос. Рассмотрим следующий код:
#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
]
Ожидается ли это? Я пишу класс, где я хотел иметь уникальный указатель в классе. При попытке выработать семантику конструктора перемещения для класса я столкнулся с этим (я предполагаю, потому что я, наконец, правильно закодировал свой конструктор перемещения, т.е. Другие ошибки были исправлены).
MSVC прав, а GCC ошибочен:
Стандарт (3.9/5):
Неполно определенные типы объектов и типы пустот являются неполными типами
Стандарт (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 p
- undefined из-за несоответствующих типов, поэтому поведение компилятора допустимо, поскольку код является ошибкой.
Примечание: это отличается от shared_ptr<void>
, поскольку в нем используется стирание стилей, чтобы отслеживать исходный тип переданного указателя.
Не удалять переменные void *
.
Если вы хотите работать с чем-то вроде Win32 Handles, пожалуйста, укажите пользовательский deleter.
Например:
void HandleDeleter(HANDLE h)
{
if (h) CloseHandle(h);
}
using UniHandle = unique_ptr<void, function<void(HANDLE)>>;
Тогда:
UniHandle ptr(..., HandleDeleter);