简体   繁体   English

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

[英]Why does move constructor affect is_assignable?

Just came from is_assignable and std::unique_ptr . 刚来自is_assignable和std :: unique_ptr @Angew tells me that because std::unique_ptr<int, do_nothing> and std::unique_ptr<int> are different types, so static_assert(not std::is_assignable<std::unique_ptr<int>, std::unique_ptr<int, do_nothing>>::value, ""); @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, ""); . So, I tried: 所以,我尝试过:

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>>);
}

Yes, because MoveAssignOnly_V2<int, float> and MoveAssignOnly_V2<int, double> are two different types, so they are not assignable. 是的,因为MoveAssignOnly_V2<int, float>MoveAssignOnly_V2<int, double>是两种不同的类型,因此它们不可分配。

But , when I add aa move ctor: 但是 ,当我添加一个移动ctor:

template<class U, class E>
MoveAssignOnly_V2(MoveAssignOnly_V2<U, E>&& m) noexcept {}

static_assert fail! static_assert失败! (both gcc and clang). (包括gcc和clang)。

Question here: Why does move constructor affects is_assignable? 问题:为什么移动构造函数会影响is_assignable?

Updated 更新

The reason why I add this constructor is that I found std::unique_ptr have a 我添加这个构造函数的原因是我发现std::unique_ptr有一个

template< class U, class E >
unique_ptr( unique_ptr<U, E>&& u ) noexcept;

, which confuses me a little: how can it be not assignable now that it has such a ctor? ,这让我有点困惑:如果它有这样一个ctor,它怎么可能不可分配? so I tried add such ctor to MoveAssignOnly_V2 and post this question. 所以我尝试将此类ctor添加到MoveAssignOnly_V2并发布此问题。 The two answers are fine, however, still cannot explain why std::unique_ptr is not assignable when it has both move assignment and this templated constructor. 这两个答案很好,但是,仍然无法解释为什么当std::unique_ptr同时具有移动赋值和这个模板化构造函数时,它是不可赋值的。

Take this code: 拿这个代码:

MoveAssignOnly_V2<int, float> lhs;
MoveAssignOnly_V2<int, double> rhs;
lhs = stdL::move(rhs);

When the converting constructor (note that it's not a move constructor) is not there, there is no way to assign rhs into lhs . 当转换构造函数(注意它不是移动构造函数)不存在时,无法将rhs分配给lhs

However, when you add the constructor template, there is now a way to convert rhs into the type MoveAssignOnly_V2<int, float> (which creates a temporary of that type). 但是,当您添加构造函数模板时,现在有一种方法可以将rhs转换为MoveAssignOnly_V2<int, float> (它创建该类型的临时值)。 Then, it's possible to move-assign from that temporary into lhs . 然后,可以从该临时值移动到lhs

This is the same principle as: 这与以下原则相同:

double lhs = 3.14;
float rhs = 42.f;
lhs = std::move(rhs);

To address the update in the question: 要解决问题中的更新:

You cannot go with function declarations alone, you have to read the full specification (in the standard or suitable reference ). 您不能单独使用函数声明,您必须阅读完整的规范(在标准或合适的参考中 )。 Quoting the linked reference about the converting constructor of std::unique_ptr : 引用有关std::unique_ptr的转换构造函数的链接引用:

This constructor only participates in overload resolution if all of the following is true: 如果满足以下所有条件,则此构造函数仅参与重载解析:

a) unique_ptr<U, E>::pointer is implicitly convertible to pointer a) unique_ptr<U, E>::pointer可以隐式转换为pointer
b) U is not an array type b) U不是数组类型
c) Either Deleter is a reference type and E is the same type as D , or Deleter is not a reference type and E is implicitly convertible to D c) Deleter是引用类型, E是与D相同的类型,或者Deleter不是引用类型, E可以隐式转换为D

So as you can see, the unique_ptr 's converting constructor must be implemented so that it's only active if the source deleter can be converted to the target one. 正如您所看到的,必须实现unique_ptr的转换构造函数,以便只有在源删除器可以转换为目标删除器时才会激活它。 That's basically the same rule as for move assignment in the previous question. 这与上一个问题中的移动分配基本相同。

What you added here: 你在这里添加的内容:

template<class U, class E>
MoveAssignOnly_V2(MoveAssignOnly_V2<U, E>&& m) noexcept {}

is not only a move constructor, but a templated constructor that can construct MoveAssignOnly_V2<T, D> from any MoveAssignOnly_V2<U, E> . 不仅是一个移动构造函数,而且是一个模板化的构造函数,可以从任何MoveAssignOnly_V2<U, E>构造MoveAssignOnly_V2<T, D> MoveAssignOnly_V2<U, E>

Thus constructing MoveAssignOnly_V2<int, float> from MoveAssignOnly_V2<int, double>> is fine. 因此MoveAssignOnly_V2<int, float>MoveAssignOnly_V2<int, double>>构造MoveAssignOnly_V2<int, float>就可以了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM