为什么移动构造函数会影响is_assignable?
Why does move constructor affect is_assignable?
刚从过来。 @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>
是两种不同的类型,所以不可赋值。
但是,当我添加一个移动构造函数时:
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,它怎么不能赋值呢?所以我尝试将这样的构造函数添加到 MoveAssignOnly_V2
和 post 这个问题。这两个答案都很好,但是,仍然无法解释为什么 std::unique_ptr
在同时具有移动赋值和此模板化构造函数时不可赋值。
取此代码:
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
:
的转换构造函数的链接参考
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
b) U
is not an array type
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
因此,如您所见,必须实现 unique_ptr
的转换构造函数,以便它仅在源删除器可以转换为目标删除器时才处于活动状态。这与上一个问题中移动分配的规则基本相同。
您在此处添加的内容:
template<class U, class E>
MoveAssignOnly_V2(MoveAssignOnly_V2<U, E>&& m) noexcept {}
不仅是一个移动构造函数,而且是一个模板构造函数,可以从任何MoveAssignOnly_V2<U, E>
.
构造MoveAssignOnly_V2<T, D>
因此从 MoveAssignOnly_V2<int, double>>
构建 MoveAssignOnly_V2<int, float>
就可以了。
刚从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>
是两种不同的类型,所以不可赋值。
但是,当我添加一个移动构造函数时:
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,它怎么不能赋值呢?所以我尝试将这样的构造函数添加到 MoveAssignOnly_V2
和 post 这个问题。这两个答案都很好,但是,仍然无法解释为什么 std::unique_ptr
在同时具有移动赋值和此模板化构造函数时不可赋值。
取此代码:
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
:
This constructor only participates in overload resolution if all of the following is true:
a)
unique_ptr<U, E>::pointer
is implicitly convertible topointer
b)U
is not an array type
c) EitherDeleter
is a reference type andE
is the same type asD
, orDeleter
is not a reference type andE
is implicitly convertible toD
因此,如您所见,必须实现 unique_ptr
的转换构造函数,以便它仅在源删除器可以转换为目标删除器时才处于活动状态。这与上一个问题中移动分配的规则基本相同。
您在此处添加的内容:
template<class U, class E>
MoveAssignOnly_V2(MoveAssignOnly_V2<U, E>&& m) noexcept {}
不仅是一个移动构造函数,而且是一个模板构造函数,可以从任何MoveAssignOnly_V2<U, E>
.
MoveAssignOnly_V2<T, D>
因此从 MoveAssignOnly_V2<int, double>>
构建 MoveAssignOnly_V2<int, float>
就可以了。