如何判断一个类型是否真正可移动构造
How to get if a type is truly move constructible
以这段代码为例:
#include <type_traits>
#include <iostream>
struct Foo
{
Foo() = default;
Foo(Foo&&) = delete;
Foo(const Foo&) noexcept
{
std::cout << "copy!" << std::endl;
};
};
struct Bar : Foo {};
static_assert(!std::is_move_constructible_v<Foo>, "Foo shouldn't be move constructible");
// This would error if uncommented
//static_assert(!std::is_move_constructible_v<Bar>, "Bar shouldn't be move constructible");
int main()
{
Bar bar {};
Bar barTwo { std::move(bar) };
// prints "copy!"
}
因为 Bar 派生自 Foo,所以它没有移动构造函数。它仍然可以通过使用复制构造函数来构造。我从另一个答案中了解到为什么它选择复制构造函数:
if y
is of type S
, then std::move(y)
, of type S&&
, is reference compatible with type S&
. Thus S x(std::move(y))
is perfectly valid and call the copy constructor S::S(const S&)
.
—Lærne,
所以我理解了为什么右值 "downgrades" 从移动到左值复制,因此 std::is_move_constructible
returns 为真。但是,有没有办法检测一个类型是否真正可移动构造(不包括复制构造函数)?
如何判断一个类型是否有移动构造函数?
假设基础 class 来自上游,派生 class 是您应用程序的一部分,一旦您决定派生 [=24],您就无法做出进一步的决定=] 来自 'their' Foo.
的栏
基础 class Foo 有责任定义自己的构造函数。那是基础 class 的一个实现细节。派生的class也是如此。构造函数不被继承。平凡地,两个 classes 都可以完全控制自己的实现。
所以,如果你想在派生的 class 中有一个移动构造函数,只需添加一个:
struct Bar : Foo {
Bar(Bar&&) noexcept {
std::cout << "move!" << std::endl;
};
};
如果您不想要任何内容,请将其删除:
struct Bar : Foo {
Bar(Bar&&) = delete;
};
如果您执行后者,您也可以取消注释第二个 static_assert 而不会出现错误。
有人声称 并且表面上它们似乎是正确的 - &&
绑定到 const&
的方式使得无法判断 [=] 中存在哪些构造函数44=]' 界面.
然后我想到 -- C++ 中的移动语义不是一个单独的语义...它是一个 "alias" 到一个复制语义,另一个 "interface" class实施者可以 "intercept" 并提供替代实施。所以问题 "can we detect a presence of move ctor?" 可以重新表述为 "can we detect a presence of two copy interfaces?"。事实证明,我们可以通过(ab)使用重载来实现这一点——当有两种同样可行的方法来构造一个对象时,它无法编译,并且可以使用 SFINAE 检测到这一事实。
30 lines of code值一千字:
#include <type_traits>
#include <utility>
#include <cstdio>
using namespace std;
struct S
{
~S();
//S(S const&){}
//S(S const&) = delete;
//S(S&&) {}
//S(S&&) = delete;
};
template<class P>
struct M
{
operator P const&();
operator P&&();
};
constexpr bool has_cctor = is_copy_constructible_v<S>;
constexpr bool has_mctor = is_move_constructible_v<S> && !is_constructible_v<S, M<S>>;
int main()
{
printf("has_cctor = %d\n", has_cctor);
printf("has_mctor = %d\n", has_mctor);
}
备注:
您可能应该能够将此逻辑与其他 const/volatile
重载混淆,因此此处可能需要一些额外的工作
怀疑这种魔法是否适用于 private/protected 构造函数——另一个需要关注的领域
似乎不适用于 MSVC(传统上如此)
以这段代码为例:
#include <type_traits>
#include <iostream>
struct Foo
{
Foo() = default;
Foo(Foo&&) = delete;
Foo(const Foo&) noexcept
{
std::cout << "copy!" << std::endl;
};
};
struct Bar : Foo {};
static_assert(!std::is_move_constructible_v<Foo>, "Foo shouldn't be move constructible");
// This would error if uncommented
//static_assert(!std::is_move_constructible_v<Bar>, "Bar shouldn't be move constructible");
int main()
{
Bar bar {};
Bar barTwo { std::move(bar) };
// prints "copy!"
}
因为 Bar 派生自 Foo,所以它没有移动构造函数。它仍然可以通过使用复制构造函数来构造。我从另一个答案中了解到为什么它选择复制构造函数:
if
y
is of typeS
, thenstd::move(y)
, of typeS&&
, is reference compatible with typeS&
. ThusS x(std::move(y))
is perfectly valid and call the copy constructorS::S(const S&)
.—Lærne,
所以我理解了为什么右值 "downgrades" 从移动到左值复制,因此 std::is_move_constructible
returns 为真。但是,有没有办法检测一个类型是否真正可移动构造(不包括复制构造函数)?
如何判断一个类型是否有移动构造函数?
假设基础 class 来自上游,派生 class 是您应用程序的一部分,一旦您决定派生 [=24],您就无法做出进一步的决定=] 来自 'their' Foo.
的栏基础 class Foo 有责任定义自己的构造函数。那是基础 class 的一个实现细节。派生的class也是如此。构造函数不被继承。平凡地,两个 classes 都可以完全控制自己的实现。
所以,如果你想在派生的 class 中有一个移动构造函数,只需添加一个:
struct Bar : Foo {
Bar(Bar&&) noexcept {
std::cout << "move!" << std::endl;
};
};
如果您不想要任何内容,请将其删除:
struct Bar : Foo {
Bar(Bar&&) = delete;
};
如果您执行后者,您也可以取消注释第二个 static_assert 而不会出现错误。
有人声称 &&
绑定到 const&
的方式使得无法判断 [=] 中存在哪些构造函数44=]' 界面.
然后我想到 -- C++ 中的移动语义不是一个单独的语义...它是一个 "alias" 到一个复制语义,另一个 "interface" class实施者可以 "intercept" 并提供替代实施。所以问题 "can we detect a presence of move ctor?" 可以重新表述为 "can we detect a presence of two copy interfaces?"。事实证明,我们可以通过(ab)使用重载来实现这一点——当有两种同样可行的方法来构造一个对象时,它无法编译,并且可以使用 SFINAE 检测到这一事实。
30 lines of code值一千字:
#include <type_traits>
#include <utility>
#include <cstdio>
using namespace std;
struct S
{
~S();
//S(S const&){}
//S(S const&) = delete;
//S(S&&) {}
//S(S&&) = delete;
};
template<class P>
struct M
{
operator P const&();
operator P&&();
};
constexpr bool has_cctor = is_copy_constructible_v<S>;
constexpr bool has_mctor = is_move_constructible_v<S> && !is_constructible_v<S, M<S>>;
int main()
{
printf("has_cctor = %d\n", has_cctor);
printf("has_mctor = %d\n", has_mctor);
}
备注:
您可能应该能够将此逻辑与其他
const/volatile
重载混淆,因此此处可能需要一些额外的工作怀疑这种魔法是否适用于 private/protected 构造函数——另一个需要关注的领域
似乎不适用于 MSVC(传统上如此)