如何获得类型是否真正可移动构造 [英] 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,所以它没有move构造函数.通过使用复制构造函数,它仍然可以构造.我了解了为什么它从另一个答案中选择了复制构造函数:
Because Bar is derived from Foo, it doesn't have a move constructor. It is still constructible by using the copy constructor. I learned why it chooses the copy constructor from another answer:
如果
y
的类型为S
,则std::move(y)
的类型为S&&
,是与S&
类型兼容的引用.因此S x(std::move(y))
是完全有效的,并调用复制构造函数S::S(const S&)
.
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,了解std::is_move_constructible
—Lærne, Understanding std::is_move_constructible
所以我理解为什么右值从降级复制到左值复制会降级,以及为什么std::is_move_constructible
返回true的原因.但是,有没有一种方法可以检测类型(复制副本构造函数除外)是否真正可移动构造?
So I understand why a rvalue "downgrades" from moving to a lvalue copying, and thus why std::is_move_constructible
returns true. However, is there a way to detect if a type is truly move constructible excluding the copy constructor?
推荐答案
There are claims that presence of move constructor can't be detected and on surface they seem to be correct -- the way &&
binds to const&
makes it impossible to tell which constructors are present in class' interface.
然后我想到了-C ++中的移动语义不是单独的语义...它是复制语义的别名",是类实现者可以拦截"并提供替代实现的另一个接口".因此,问题我们可以检测到存在移动ctor吗?"可以重新表述为我们可以检测到两个复制接口的存在吗?".事实证明,我们可以通过(ab)使用重载来实现这一点-当有两种同样可行的方法构造对象时,它无法编译,并且可以使用SFINAE来检测到这一事实.
Then it occurred to me -- move semantic in C++ isn't a separate semantic... It is an "alias" to a copy semantic, another "interface" that class implementer can "intercept" and provide alternative implementation. So the question "can we detect a presence of move ctor?" can be reformulated as "can we detect a presence of two copy interfaces?". Turns out we can achieve that by (ab)using overloading -- it fails to compile when there are two equally viable ways to construct an object and this fact can be detected with SFINAE.
30行代码值得一千个单词:
#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
重载混淆,因此此处可能需要做一些额外的工作
you probably should be able to confuse this logic with additional
const/volatile
overloads, so some additional work may be required here
怀疑此魔术在私有/受保护的构造函数中效果很好-另一个需要研究的领域
doubt this magic works well with private/protected constructors -- another area to look at
似乎在MSVC上不起作用(传统上如此)
doesn't seem to work on MSVC (as is tradition)
这篇关于如何获得类型是否真正可移动构造的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!