如何获得类型是否真正可移动构造 [英] How to get if a type is truly move constructible

查看:91
本文介绍了如何获得类型是否真正可移动构造的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下面的代码为例:

#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 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,了解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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆