【问题标题】:Are all CopyConstructible types MoveConstructible types?所有 CopyConstructible 类型都是 MoveConstructible 类型吗?
【发布时间】:2016-05-04 15:58:42
【问题描述】:

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

N3337:

表 21 — CopyConstructible 要求(除了 MoveConstructible) [copyconstructible] [...]

cppreference.com:

类型 T 满足 CopyConstructible if

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

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

main.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 包含错误?

【问题讨论】:

    标签: c++ c++11 ubuntu gcc


    【解决方案1】:

    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 ]
    

    除了[copyconstructible]

    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&lt;As&gt;::value 为假的原因是您有一个已删除的移动构造函数,它禁止移动构造。必须具有未删除的移动构造函数并满足 [moveconstructible] 才能使其为真。

    【讨论】:

      【解决方案2】:

      你的例子有点误导你。

      As(As&&)=delete;
      

      通过删除移动构造函数,您将使用As&amp;&amp; 构造一个As 变为非法,即使复制构造函数可以被调用,因为它采用了对常量的引用。

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

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

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

      Live Demo


      *具体来说,如果存在用户声明的拷贝构造函数、拷贝赋值运算符、移动赋值运算符或析构函数,则移动构造函数不会被隐式声明。

      【讨论】:

        【解决方案3】:

        std::is_copy_constructible&lt;T&gt; 被定义为精确的 std::is_constructible&lt;T, const T&amp;&gt;,即它只测试从 const 左值构造的可能性,它不测试 CopyConstructible 概念的所有属性。

        所以您的测试并没有显示您认为它显示的内容。您的类型不是 CopyConstructible 类型,因为它不符合其他一些要求。

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-05-24
          • 1970-01-01
          • 2012-07-17
          • 1970-01-01
          • 2019-12-10
          • 1970-01-01
          • 2016-09-08
          • 1970-01-01
          相关资源
          最近更新 更多