c++ - 如何判断一个类型是否真正可 move 构造

标签 c++ stl move-semantics typetraits

以这段代码为例:

#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 构造函数。它仍然可以通过使用复制构造函数来构造。我从另一个答案中了解到为什么它选择复制构造函数:

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, Understanding std::is_move_constructible

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

最佳答案

有人声称presence of move constructor can't be detected从表面上看,它们似乎是正确的——&& 绑定(bind)到 const& 的方式使得无法判断哪些构造函数存在于类的接口(interface)中。

然后我想到——C++ 中的 move 语义不是一个单独的语义……它是复制语义的“别名”,是类实现者可以“拦截”并提供替代实现的另一个“接口(interface)”。所以问题是“我们能检测到 move ctor 的存在吗?”可以重新表述为“我们可以检测到两个复制接口(interface)的存在吗?”。事实证明,我们可以通过(滥用)使用重载来实现这一点——当有两种同样可行的方法来构造一个对象时,它无法编译,并且可以使用 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(传统)

关于c++ - 如何判断一个类型是否真正可 move 构造,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51901837/

相关文章:

c++ - 我应该 'delete' 这个 CDC 吗?

c++ - 如何避免使用 std::bind() 临时对象进行显式转换?

c++ - 从 r-value ref-qualified 方法 move 还是不 move ?

c++ - 为什么vector前面没有push/pop?

c++ - std::move 如何使原始变量的值无效?

c++11,抢救一个集合

c++ - 用户定义的容器不适用于 std::ranges

c++ - C++ 最常用的 XML 库是什么?

c++ - gcc code::blocks 共享库问题

c++ - 克服 C++ 中错误的内存分配