【问题标题】:Why does move constructor affect is_assignable?为什么移动构造函数会影响 is_assignable?
【发布时间】:2018-12-21 13:23:50
【问题描述】:

刚来自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&lt;int, float&gt;MoveAssignOnly_V2&lt;int, double&gt; 是两种不同的类型,所以它们是不可赋值的。

但是,当我添加一个移动 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 在同时具有移动赋值和这个模板化构造函数时是不可赋值的。

【问题讨论】:

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


    【解决方案1】:

    获取此代码:

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

    当转换构造函数(注意它不是移动构造函数)不存在时,无法将rhs 分配给lhs

    但是,当您添加构造函数模板时,现在可以将rhs 转换为MoveAssignOnly_V2&lt;int, float&gt; 类型(创建该类型的临时)。然后,可以从该临时地址移动分配到lhs

    这与以下原理相同:

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

    解决问题中的更新:

    你不能单独使用函数声明,你必须阅读完整的规范(标准或suitable reference)。引用std::unique_ptr的转换构造函数的链接参考:

    仅当以下所有条件都为真时,此构造函数才参与重载决议:

    a) unique_ptr&lt;U, E&gt;::pointer 可隐式转换为 pointer
    b) U 不是数组类型
    c) Deleter 是引用类型并且E 是与D 相同的类型,或者Deleter 不是引用类型并且E 可以隐式转换为D

    如您所见,unique_ptr 的转换构造函数必须实现,以便它只有在源删除器可以转换为目标删除器时才有效。这与上一个问题中移动分配的规则基本相同。

    【讨论】:

    • 请查看问题中的更新。我注意到 unique_ptr 也有这样的转换构造函数,但是,与 MoveAssignOnly_V2 不同,unique_ptr 仍然不可赋值。
    【解决方案2】:

    您在此处添加的内容:

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

    不仅是一个移动构造函数,而且是一个模板化构造函数,可以从任何MoveAssignOnly_V2&lt;U, E&gt;构造MoveAssignOnly_V2&lt;T, D&gt;

    因此从MoveAssignOnly_V2&lt;int, double&gt;&gt; 构造MoveAssignOnly_V2&lt;int, float&gt; 很好。

    【讨论】:

    • 严格来说,它根本不是移动构造函数。
    • 您可能希望将其称为转换构造函数,它允许转换为可分配的类型。
    猜你喜欢
    • 2018-02-24
    • 1970-01-01
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 2019-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多