为什么移动构造函数会影响is_assignable?

时间:2018-12-21 13:23:51

标签: c++ assignment-operator move-constructor

只是来自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既具有移动分配又具有此模板构造函数时却无法分配。

2 个答案:

答案 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是引用类型,ED是相同类型,或者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>>