只是来自is_assignable and std::unique_ptr。 @Angew告诉我,因为std::unique_ptr<int, do_nothing>
和std::unique_ptr<int>
是不同的类型,所以static_assert(not std::is_assignable<std::unique_ptr<int>, std::unique_ptr<int, do_nothing>>::value, "");
。所以,我尝试了:
template<typename T, typename D>
struct MoveAssignOnly_V2
{
MoveAssignOnly_V2&
operator=(MoveAssignOnly_V2&)
= delete;
MoveAssignOnly_V2&
operator=(MoveAssignOnly_V2&&) noexcept
{}
};
int main()
{
static_assert(not std::is_assignable_v<MoveAssignOnly_V2<int, float>,
MoveAssignOnly_V2<int, double>>);
}
是的,因为MoveAssignOnly_V2<int, float>
和MoveAssignOnly_V2<int, double>
是两种不同的类型,所以它们是不可分配的。
但是,当我添加移动ctor时:
template<class U, class E>
MoveAssignOnly_V2(MoveAssignOnly_V2<U, E>&& m) noexcept {}
static_assert
fail!(gcc和clang)。
这里的问题:为什么移动构造函数会影响is_assignable?
我添加此构造函数的原因是我发现std::unique_ptr
具有
template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;
,这让我有些困惑:既然它有这样的ctor,怎么又不能分配?因此我尝试将此类ctor添加到MoveAssignOnly_V2
并发布此问题。这两个答案很好,但是仍然无法解释为什么std::unique_ptr
既具有移动分配又具有此模板构造函数时却无法分配。
答案 0 :(得分:4)
使用此代码:
MoveAssignOnly_V2<int, float> lhs;
MoveAssignOnly_V2<int, double> rhs;
lhs = stdL::move(rhs);
如果不存在转换构造函数(请注意,它不是移动构造函数),则无法将rhs
分配给lhs
。
但是,当您添加构造函数模板时,现在有一种方法可以将rhs
转换为类型MoveAssignOnly_V2<int, float>
(将创建该类型的临时类型)。然后,可以将其从该临时目录移动到lhs
。
这与以下相同:
double lhs = 3.14;
float rhs = 42.f;
lhs = std::move(rhs);
要解决问题中的更新,请执行以下操作:
不能单独使用函数声明,而必须阅读完整的规范(在标准或suitable reference中)。引用有关std::unique_ptr
的转换构造函数的链接参考:
只有满足以下所有条件,此构造函数才参与重载解析:
a)
unique_ptr<U, E>::pointer
可隐式转换为pointer
b)U
不是数组类型
c)Deleter
是引用类型,E
与D
是相同类型,或者Deleter
不是引用类型,并且E
可隐式转换为D
因此,如您所见,必须实现unique_ptr
的转换构造函数,以便仅在源删除程序可以转换为目标删除程序的情况下才有效。这基本上与上一个问题中的移动分配规则相同。
答案 1 :(得分:2)
您在此处添加的内容:
template<class U, class E>
MoveAssignOnly_V2(MoveAssignOnly_V2<U, E>&& m) noexcept {}
不仅是move构造函数,而且是可以从任何MoveAssignOnly_V2<T, D>
构造MoveAssignOnly_V2<U, E>
的模板化构造函数。
因此可以从MoveAssignOnly_V2<int, float>
构造MoveAssignOnly_V2<int, double>>
。