26

这是一个非常简单的问题。考虑以下代码:

#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
        ]

这是预期的吗?我正在写一个类,我想在类中有一个唯一的指针。在尝试为类计算移动构造函数的语义时,我遇到了这个问题(我假设是因为我终于正确地编码了移动构造函数:即其他错误已修复)。

4

4 回答 4

22

MSVC 是对的,而 GCC 是错的:

标准(3.9/5):

未完全定义的对象类型和 void 类型是不完全类型

标准(20.7.1.1.2/4):

如果 T 是不完全类型,则程序是非良构的

于 2013-11-07T16:40:35.730 回答
22

GCC 实际上有代码来防止它,但它直到最近才起作用。

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

因此,按照标准的要求,使用中继上的最新代码,您的示例无法编译。

于 2013-11-08T16:07:38.900 回答
8

问题归结为:

void* p = new int;
delete p;

查看 n3797 5.3.5 删除,我认为delete p由于类型不匹配,这是未定义的行为,因此任何编译器行为都是可以接受的,因为代码有问题。

注意:这与 不同shared_ptr<void>,因为它使用类型擦除来跟踪传入的指针的原始类型。

于 2013-11-07T16:31:54.773 回答
3

不要删除 的变量void *。</p>

如果您想使用 Win32 Handles 之类的东西,请提供自定义删除器。

例如:

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

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

然后:

UniHandle ptr(..., HandleDeleter);
于 2016-03-28T03:27:37.093 回答