如何判断一个类型是否真正可移动构造

2024-04-18

以这段代码为例:

#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属于类型S, then std::move(y),类型S&&, 引用与类型兼容S&. Thus S x(std::move(y))完全有效并调用复制构造函数S::S(const S&).

—Lærne, 理解std::is_move_constructible https://stackoverflow.com/a/33939866/1248889

所以我理解为什么右值从移动到左值复制“降级”,以及为什么std::is_move_constructible返回真。但是,有没有一种方法可以检测类型是否真正可移动构造(不包括复制构造函数)?


有说法称无法检测到移动构造函数的存在 https://stackoverflow.com/questions/42423146/how-to-detect-whether-a-class-has-a-move-constructor从表面上看,他们似乎是正确的——方式&&绑定到const&使得无法判断类的接口中存在哪些构造函数。

然后我突然想到——C++ 中的移动语义不是一个单独的语义...它是复制语义的“别名”,是类实现者可以“拦截”并提供替代实现的另一个“接口”。所以问题是“我们能检测到移动向量的存在吗?”可以重新表述为“我们可以检测到两个复制接口的存在吗?”。事实证明,我们可以通过(ab)使用重载来实现这一点——当有两种同样可行的方法来构造对象时,它无法编译,并且可以使用 SFINAE 检测到这一事实。

30行代码 http://coliru.stacked-crooked.com/a/5e8b7f4a40c84a86胜过千言万语:

#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);
}

Notes:

  • 您可能应该能够将此逻辑与其他逻辑混淆const/volatile重载,因此这里可能需要一些额外的工作

  • 怀疑这种魔法是否适用于私有/受保护的构造函数——另一个值得关注的领域

  • 似乎不适用于 MSVC(按照传统)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何判断一个类型是否真正可移动构造 的相关文章

随机推荐