我有一个基类及其子类:
std::unique_ptr<Base> GetDerived() {
return std::make_unique<Derived>();
}
尝试创建一个辅助函数,该函数创建Derived对象的唯一指针。
1)这个有效:
std::unique_ptr<Base> GetDerived2() {
auto a = std::make_unique<Derived>();
return a;
}
2)但是,这个没有编译:
std::unique_ptr<Base> GetDerived3() {
auto a = std::make_unique<Derived>();
return std::move(a);
}
3)std :: move有效:
std::unique_ptr<Base> GetDerived4() {
auto a = std::make_unique<Base>();
return a;
}
std::unique_ptr<Base> GetDerived5() {
auto a = std::make_unique<Base>();
return std::move(a);
}
4)如果我创建了一个Base实例,那么两者都有效:
ImportError: No module named 'winrandom'
为什么(2)失败但其他人工作?
答案 0 :(得分:10)
std::unique_ptr
不可复制,只能移动。您可以return std::make_unique<Derived>
从声明返回std::unique_ptr<Base>
的函数中获取的原因是,从一个函数转换为另一个函数。
所以1)相当于:
std::unique_ptr<Base> GetDerived() {
return std::unique_ptr<Base>(std::made_unique<Derived>());
}
由于std::make_unique
返回的值是一个右值,因此返回值是移动构造的。
对比2),相当于:
std::unique_ptr<Base> GetDerived2() {
std::unique_ptr<Derived> a = std::make_unique<Derived>();
return std::unique_ptr<Base>(a);
}
由于a
是左值,因此返回值必须是复制构造的,std::unique_ptr
是不可复制的。
3)之所以有效是因为你将左值a
转换为右值,并且返回值可以移动构造。
4)和5)工作是因为你已经有std::unique_ptr<Base>
并且不需要构建一个返回。
答案 1 :(得分:1)
std::unique_ptr<>
没有复制构造函数,但它确实有一个来自相关指针的移动构造函数,即
unique_ptr( unique_ptr&& u ); // move ctor
template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ); // move ctor from related unique_ptr
第二个构造函数需要某些条件(参见here)。那么为什么你的代码2不起作用,但是4呢?在4中,您没有使用任何构造函数,因为返回类型与对象相同,返回了对象本身。另一方面,在2中,返回类型不同,需要构造函数调用,但需要std::move()
。
答案 2 :(得分:1)
在每种情况下,但(2)返回的值被视为(某种)rvalue。在(2)中它不是,因为类型不匹配隐式移动被阻止。
在标准的后续迭代中,(2)也会隐式移动。
所有这些都会在被调用后很快进入未定义的行为,因为他们试图通过指针 - Derived
删除Base
对象。要解决此问题,请记录删除功能。
template<class T>
using smart_unique=std::unique_ptr<T, void(*)(void*)>;
template<class T, class...Args>
smart_unique<T> make_smart_unique( Args&&... args ){
return {
new T(std::forward<Args>(args)...),
[](void*ptr){ delete static_cast<T*>(ptr); }
};
}
template<class T>
static const smart_unique<T> empty_smart_unique{ nullptr, [](void*){} };
这些是足以处理多态性的独特指针,如shared_ptr
可以。
答案 3 :(得分:0)
在上面列出的示例中,(1)返回一个右值,但(2)不是右值,并且正在尝试对unique_ptr进行复制,而对于unique_ptr则无法复制。
使用move是有效的,因为您将此时的unique_ptr视为右值。
答案 4 :(得分:-1)
如果您仔细研究std :: unique_ptr的定义,就会发现它可以从派生的转移到基数。基本上is_convertible
在这里检查这种情况。
/** @brief Converting constructor from another type
*
* Requires that the pointer owned by @p __u is convertible to the
* type of pointer owned by this object, @p __u does not own an array,
* and @p __u has a compatible deleter type.
*/
template<typename _Up, typename _Ep, typename = _Require<
__safe_conversion_up<_Up, _Ep>,
typename conditional<is_reference<_Dp>::value,
is_same<_Ep, _Dp>,
is_convertible<_Ep, _Dp>>::type>>
unique_ptr(unique_ptr<_Up, _Ep>&& __u) noexcept
: _M_t(__u.release(), std::forward<_Ep>(__u.get_deleter()))
{ }