都是 CopyConstructible 类型 MoveConstructible 类型

Are all CopyConstructible types MoveConstructible types?

本文关键字:类型 MoveConstructible CopyConstructible 都是      更新时间:2023-10-16

根据工作草案N3337(与已发布的ISOC++11标准最相似的草案)和 cppreference.com,答案是肯定的。

N3337:

表 21 — 可复制可构造要求(除 MoveConstructible) [copyconstructible] [...]

cppreference.com:

类型 T 满足 CopyConstructible 如果

  • 类型 T 满足 MoveConstructible,并且 [...]

但根据编译 main.cpp 与 gcc(Ubuntu 4.8.4-2ubuntu1~14.04)4.8.4 并使用 Ubuntu 14.04.3 LTS 中引用的语句运行 a.out 的结果,答案是否定的。

主.cpp:

#include <iostream>
#include <type_traits>
struct As
{
    As()=default;
    As(As&&)=delete;
    As(const As&)=default;
    As& operator=(As&&)=delete;
    As& operator=(const As&)=delete;
    ~As()=default;
};
int main()
{
    std::cout<<std::is_move_constructible<As>::value<<std::endl;
    std::cout<<std::is_copy_constructible<As>::value<<std::endl;
    return 0;
}

从终端编译和运行:

$ g++ -std=c++11 main.cpp
$ ./a.out

结果(输出):

0
1

我是否误解了什么,或者 N3337 和 cppreference.com 错了,或者 gcc 是否包含错误?

std::is_copy_constructible<T>被定义为完全std::is_constructible<T, const T&>即它只测试从常量左值构造是可能的,它不测试CopyConstructible概念的所有属性。

因此,您的测试不会显示您认为它显示的内容。您的类型不是 CopyConstructible 类型,因为它不符合其他一些要求。

至于最初的问题,是的。因为所有 CopyConstructible 类型都必须满足 MoveConstructible 的要求,所以它们都是 MoveConstructible 类型。MoveConstructible 不需要移动任何东西,只需要从右值构造是可能的,并且所有 CopyConstructible 类型都可以从右值构造(即使它们可能执行深层复制而不是移动)。

您可以创建可以从左值

复制但不能从右值复制的反常类型,或者可以从常量左值复制但不能从非常量左值和其他可憎的东西复制。此类类型不是 CopyConstructible,并且不能很好地与C++标准库配合使用。很少有充分的理由来创建这样的反常类型。

你的例子有些误导你。

As(As&&)=delete;

通过删除移动构造函数,你使得构造一个带有As&&As是非法的,即使复制构造函数可以调用,因为它需要对常量引用。

显示您正在寻找的行为的示例如下:

struct As
{
    As()=default;
    As(const As&)=default;
    As& operator=(As&&)=delete;
    As& operator=(const As&)=delete;
    ~As()=default;
};

我刚刚删除了移动构造函数的删除。 As不会隐式声明 move 构造函数,因为它具有一堆其他用户声明的特殊函数*。如果对此示例运行测试,您将看到该类是可构造的,即使它没有移动构造函数也是如此。

现场演示


*具体而言,如果存在用户声明的复制构造函数、复制赋值运算符、移动赋值运算符或析构函数,则不会隐式声明移动构造函数。

is_copy_constructible不需要

类型是可移动的。 当它说

CopyConstructible 需求(除了 MoveConstructible) [copyconstructible]

这意味着要成为 CopyConstructible,该类必须满足 MoveConstructible 的要求,这些要求是

T u = rv;       u is equivalent to the value of rv before the construction
T(rv)           T(rv) is equivalent to the value of rv before the construction
rv’s state is unspecified. [ Note: rv must still meet the requirements of the
library component that is using it. The operations listed in those requirements
must work as specified whether rv has been moved from or not. — end note ]

除了 [可复制构造]

T u = v;        the value of v is unchanged and is equivalent to u
T(v)            the value of v is unchanged and is equivalent to T(v)

std::is_move_constructible<As>::value 是错误的原因是您有一个已删除的移动构造器,它禁止移动构造。需要有一个未删除的移动构造函数并满足 [moveconstructible] 才能为真。