std::unique_ptr 在 GCC 中工作,但在 Visual Studio 中无法编译
std::unique_ptr works in GCC but won't compile in Visual Studio
这花了我一段时间,但我终于构建了一个最小的示例来说明我遇到的问题。
#include <memory>
#include <vector>
class Thing
{
};
class Box
{
public:
std::vector<std::unique_ptr<Thing>> Things;
static Box MakeBox() {Box b; return b;}
};
我的真实程序显然比这个复杂很多
GCC 4.8.3 愉快地编译了它。它还编译了真正的应用程序,完美运行。
Visual Studio 2012 坚持 此代码不正确,在 vc\include\xmemory0
的第 606 行给我错误 C2248。如果我浏览几英里的编译器输出,我会发现 真正的 错误源是上例中的第 11 行。 (定义Things.
的那一行)VS也拒绝编译我的真实应用,同样的错误。
那么,这段代码正确与否?如果它不正确,那么为什么 GCC 会接受它呢?以及如何使其正确?如果是正确,那为什么VS编译不出来呢?有什么方法可以无条件地 force VS 实际编译我的程序吗?
VS 的输出:
1>------ Build started: Project: TestUniquePtr, Configuration: Debug Win32 ------
1> Main.cpp
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(606): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
1> with
1> [
1> _Ty=Thing
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\memory(1447) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
1> with
1> [
1> _Ty=Thing
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(605) : while compiling class template member function 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)'
1> with
1> [
1> _Ty=std::unique_ptr<Thing>
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(751) : see reference to function template instantiation 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' being compiled
1> with
1> [
1> _Ty=std::unique_ptr<Thing>
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\type_traits(743) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1> with
1> [
1> _Ty=std::unique_ptr<Thing>
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\vector(655) : see reference to class template instantiation 'std::is_empty<_Ty>' being compiled
1> with
1> [
1> _Ty=std::allocator<std::unique_ptr<Thing>>
1> ]
1> d:\projects\c++\testuniqueptr\testuniqueptr\main.cpp(11) : see reference to class template instantiation 'std::vector<_Ty>' being compiled
1> with
1> [
1> _Ty=std::unique_ptr<Thing>
1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
您的问题不是 std::unique_ptr
,而是 std::vector
。
您的编译器带有旧版本的 std::vector
,它要求元素类型是可复制的。
按值 (return b;
) 的 return 应该调用向量的移动,但是您的 std::vector
没有实现移动。
std::unique_ptr
可移动但不可复制,因此它不满足在 std::vector
中使用的 C++11 之前的要求...仍然适用于 VC++ 2012.
您最好的选择是使用更新的编译器和标准库。一种支持 std::vector
.
上的移动语义
否则,您可能会通过消除 std::vector
的副本取得一些进展,例如,让 MakeBox
填写输出参数而不是 return 创建新对象。
static void MakeBox(Box& b) { /* fill Things in b provided by caller */ }
虽然这可能是徒劳的,因为每当向量需要增长时,它必须将现有元素重新定位到新存储,并且在不完整的移动支持下,它会尝试复制这些元素。
问题是 Box 没有移动构造函数,因此返回一个 Box 需要它有一个复制构造函数(它不能,因为 unique_ptr 不可复制)。您所要做的就是为 Box 定义一个移动构造函数:
Box::Box(Box&& other)
: Things(std::move(other.Things))
{
}
在更新的版本中,编译器将为您生成移动构造函数。
这花了我一段时间,但我终于构建了一个最小的示例来说明我遇到的问题。
#include <memory>
#include <vector>
class Thing
{
};
class Box
{
public:
std::vector<std::unique_ptr<Thing>> Things;
static Box MakeBox() {Box b; return b;}
};
我的真实程序显然比这个复杂很多
GCC 4.8.3 愉快地编译了它。它还编译了真正的应用程序,完美运行。
Visual Studio 2012 坚持 此代码不正确,在 vc\include\xmemory0
的第 606 行给我错误 C2248。如果我浏览几英里的编译器输出,我会发现 真正的 错误源是上例中的第 11 行。 (定义Things.
的那一行)VS也拒绝编译我的真实应用,同样的错误。
那么,这段代码正确与否?如果它不正确,那么为什么 GCC 会接受它呢?以及如何使其正确?如果是正确,那为什么VS编译不出来呢?有什么方法可以无条件地 force VS 实际编译我的程序吗?
VS 的输出:
1>------ Build started: Project: TestUniquePtr, Configuration: Debug Win32 ------
1> Main.cpp
1>c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(606): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
1> with
1> [
1> _Ty=Thing
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\memory(1447) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
1> with
1> [
1> _Ty=Thing
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(605) : while compiling class template member function 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)'
1> with
1> [
1> _Ty=std::unique_ptr<Thing>
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\xmemory0(751) : see reference to function template instantiation 'void std::allocator<_Ty>::construct(_Ty *,const _Ty &)' being compiled
1> with
1> [
1> _Ty=std::unique_ptr<Thing>
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\type_traits(743) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1> with
1> [
1> _Ty=std::unique_ptr<Thing>
1> ]
1> c:\program files (x86)\microsoft visual studio 11.0\vc\include\vector(655) : see reference to class template instantiation 'std::is_empty<_Ty>' being compiled
1> with
1> [
1> _Ty=std::allocator<std::unique_ptr<Thing>>
1> ]
1> d:\projects\c++\testuniqueptr\testuniqueptr\main.cpp(11) : see reference to class template instantiation 'std::vector<_Ty>' being compiled
1> with
1> [
1> _Ty=std::unique_ptr<Thing>
1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
您的问题不是 std::unique_ptr
,而是 std::vector
。
您的编译器带有旧版本的 std::vector
,它要求元素类型是可复制的。
按值 (return b;
) 的 return 应该调用向量的移动,但是您的 std::vector
没有实现移动。
std::unique_ptr
可移动但不可复制,因此它不满足在 std::vector
中使用的 C++11 之前的要求...仍然适用于 VC++ 2012.
您最好的选择是使用更新的编译器和标准库。一种支持 std::vector
.
否则,您可能会通过消除 std::vector
的副本取得一些进展,例如,让 MakeBox
填写输出参数而不是 return 创建新对象。
static void MakeBox(Box& b) { /* fill Things in b provided by caller */ }
虽然这可能是徒劳的,因为每当向量需要增长时,它必须将现有元素重新定位到新存储,并且在不完整的移动支持下,它会尝试复制这些元素。
问题是 Box 没有移动构造函数,因此返回一个 Box 需要它有一个复制构造函数(它不能,因为 unique_ptr 不可复制)。您所要做的就是为 Box 定义一个移动构造函数:
Box::Box(Box&& other)
: Things(std::move(other.Things))
{
}
在更新的版本中,编译器将为您生成移动构造函数。