为什么 std::is_move_constructible<S>::value == false 尽管 S 移动构造很好?什么是正确的行为?
Why is std::is_move_constructible<S>::value == false despite S move-constructing just fine? What is the correct behavior?
以下代码通过 const &&
.
移动构造 S
然而它returns0
,表明S
不是可移动构造的!
根据标准,main
中的 2 个标记结构各自的正确行为是什么?
如果返回 0
是正确的行为,其背后的基本原理是什么?
(为什么它不应该反映该类型实际上是否可以通过移动构造?)
#include <algorithm>
struct S
{
S( ) { }
S(S &) { } // = delete; doesn't make any difference
S(S const &) { } // = delete; doesn't make any difference
S(S const &&) { }
S(S &&) = delete;
};
int main()
{
S const s1;
S s2(std::move(s1)); // OK for >= C++11
S s3((S())); // OK for >= C++17, error for <= C++14
static_assert(std::is_move_constructible<S>::value);
}
虽然 the standard deems S(const S&&)
to be "a move constructor" 并且您可能认为这将结束,但 is_move_constructible
实际上要求您对 s3
的声明在 C++14 及更早版本中有效;即,将为该声明选择一个移动构造函数。
If T
is not a referenceable type (i.e., possibly cv-qualified void
or a function type with a cv-qualifier-seq or a ref-qualifier), provides a member constant value equal to false
. Otherwise, provides a member constant value equal to std::is_constructible<T, T&&>::value
.
你的问题是S(S&&)
can still be found by overload resolution。在发现 S(S&&)
被删除之前,S(S&&)
比 S(const S&&)
更匹配,会发生这种情况。
s3
的声明在 C++17 中有效只是因为根本没有复制或移动进入它。曾经是临时对象的“声明”,现在只是一个奇特的构造函数参数列表;一个匹配的临时文件,当且仅当完全需要一个时,才会在调用堆栈的下方“具体化”。这被称为“保证省略”,即使没有临时对象真正被省略,因为它根本不存在。
记住移动语义是一种幻觉;您真正要做的就是让语言“调用函数”,并为您作为参数提供的表达式提供适当的参数。 s2
之所以有效,是因为您传递了一个 rvalue const S
(通过提供 const S&&
形成的表达式的值类别和类型),它可以绑定到一个 const S&&
(但 不是 到一个 S&&
!);这是否算作“move-constructing just fine”是一个角度问题。
tl;dr:该特性需要 S(S&&)
才能工作,而您明确定义它不需要。
以下代码通过 const &&
.
移动构造 S
然而它returns0
,表明S
不是可移动构造的!
根据标准,
main
中的 2 个标记结构各自的正确行为是什么?如果返回
0
是正确的行为,其背后的基本原理是什么?
(为什么它不应该反映该类型实际上是否可以通过移动构造?)
#include <algorithm>
struct S
{
S( ) { }
S(S &) { } // = delete; doesn't make any difference
S(S const &) { } // = delete; doesn't make any difference
S(S const &&) { }
S(S &&) = delete;
};
int main()
{
S const s1;
S s2(std::move(s1)); // OK for >= C++11
S s3((S())); // OK for >= C++17, error for <= C++14
static_assert(std::is_move_constructible<S>::value);
}
虽然 the standard deems S(const S&&)
to be "a move constructor" 并且您可能认为这将结束,但 is_move_constructible
实际上要求您对 s3
的声明在 C++14 及更早版本中有效;即,将为该声明选择一个移动构造函数。
If
T
is not a referenceable type (i.e., possibly cv-qualifiedvoid
or a function type with a cv-qualifier-seq or a ref-qualifier), provides a member constant value equal tofalse
. Otherwise, provides a member constant value equal tostd::is_constructible<T, T&&>::value
.
你的问题是S(S&&)
can still be found by overload resolution。在发现 S(S&&)
被删除之前,S(S&&)
比 S(const S&&)
更匹配,会发生这种情况。
s3
的声明在 C++17 中有效只是因为根本没有复制或移动进入它。曾经是临时对象的“声明”,现在只是一个奇特的构造函数参数列表;一个匹配的临时文件,当且仅当完全需要一个时,才会在调用堆栈的下方“具体化”。这被称为“保证省略”,即使没有临时对象真正被省略,因为它根本不存在。
记住移动语义是一种幻觉;您真正要做的就是让语言“调用函数”,并为您作为参数提供的表达式提供适当的参数。 s2
之所以有效,是因为您传递了一个 rvalue const S
(通过提供 const S&&
形成的表达式的值类别和类型),它可以绑定到一个 const S&&
(但 不是 到一个 S&&
!);这是否算作“move-constructing just fine”是一个角度问题。
tl;dr:该特性需要 S(S&&)
才能工作,而您明确定义它不需要。