【问题标题】:Why does a CopyConstructible type also have to be MoveConstructible?为什么 CopyConstructible 类型也必须是 MoveConstructible?
【发布时间】:2019-02-06 11:24:35
【问题描述】:

cppreference 中所述,T 类型必须是CopyConstructible 的要求是它也是MoveConstructible

STL CopyConstructible 概念的草案包含:

template <class T>
concept CopyConstructible =
   std::MoveConstructible<T> &&
   std::Constructible<T, T&> && std::ConvertibleTo<T&, T> &&
   std::Constructible<T, const T&> && std::ConvertibleTo<const T&, T> &&
   std::Constructible<T, const T> && std::ConvertibleTo<const T, T>;

它支持命名的需求语句。给定上面的定义,一个像这样的类型:

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

简单测试失败:

static_assert(std::CopyConstructible<HaveCopy>);

虽然它通过了旧的:

static_assert(std::is_copy_constructible<HaveCopy>::value);

那么,问题是为什么?标准委员会在这个问题上的意图是什么? HaveCopy 不可移动构造,但在我看来几乎可以复制构造,std::is_copy_constructible&lt;&gt; 同意我的观点。

Copyable 概念也继承了相同的行为,即:

template <class T>
concept Copyable =
   std::CopyConstructible<T> &&
   std::Movable<T> &&
   std::Assignable<T&, const T&>;

所以测试:

static_assert(std::Copyable<HaveCopy>);

也会失败。这次失败加倍。 CopyConstrucible&lt;&gt;Movable&lt;&gt; 都不同意 HaveCopy 是可复制的。

here 的讨论有点相似,但没有回答为什么。为什么我们需要这种行为?这种检查是否排除了有效的可复制构造类型,或者 HaveCopy 根本不能复制构造?如果是真的,最后一个对我来说真的很奇怪。

有什么想法吗?

【问题讨论】:

  • Jonathan Wakely 在链接线程中的回答是否也回答了为什么部分? “MoveConstructible 不需要移动任何东西,只有从右值构造是可能的,并且所有 CopyConstructible 类型都可以从右值构造(即使它们可能执行深层复制而不是移动)。”
  • 我的理解是,当某些东西被移动时,你不应该关心它实际上是被移动还是被复制,因此如果某物是CopyConstructible,它自动是MoveConstructible,你无法区分,这并不是真正的额外要求
  • 我同意 lubr,并将此问题标记为重复,您认为链接的答案没有回答什么部分?
  • 虽然我很可能不太了解它...... Wakely 的回答说“我们试图避免反常类型 [和/所以],我们还需要移动构造函数,因为有人可能会使用要复制吗?”。为什么我们通过询问 MoveConstructible 概念来做到这一点?为什么在这样做时我们将 HaveCopy 排除在 CopyConstructible 之外?
  • 这就像在问“为什么所有能被 4 整除的数字都必须能被 2 整除?”。由于T &amp;&amp;const T &amp; 的“隐式转换”,任何可以从const T &amp; 实例化的T 都可以从T &amp;&amp; 实例化。 “可以从 T &amp;&amp; 实例化”是 MoveConstructible 的定义方式

标签: c++ c++-concepts c++20


【解决方案1】:

是的,CopyConstructible 这个概念与类型特征 std::is_copy_constructible 完全不同。您专注于移动构造函数,但还有许多其他情况需要考虑。你认为这种类型应该是 CopyConstructible 吗?

struct A {
    A(A&) = delete;
    A(A const&);
};

这个怎么样?

struct B {
    explicit B(B const&);
};

关键是,您可以编写无数种构造函数组合。这并不意味着它们都有意义或值得支持。拥有一个带有复制构造函数但删除了移动构造函数的类型根本没有意义。

概念不仅仅是进行语法检查,它还涉及强制语义要求以推动有意义的类型 - 最终更容易编码。如果您只是检查is_copy_constructible,那么您所允许自己做的就是显式 从一个 const 左值构造您的类型。写T x = y;,即使yconst T,也已经超出了这个范围!这可能是复制可构造的字面意思,但它比更广泛的“我可以从T 构造T”的意义要小得多——这更接近我们在考虑复制时的真正想法。这就是CopyConstructible 给我们的概念。

当您浏览该库时,与直接翻译其名称所暗示的相比,还有一些其他概念需要更多(句法和语义上)。 EqualityComparableWith&lt;T,U&gt; 不仅检查我是否可以写t == u,还检查u == tt != uu != tStrictTotallyOrdered 不仅检查排序运算符,检查==。拥有一个有凝聚力的整体很重要。

【讨论】:

  • 那么一切都与措辞有关吗?如果是这样,请考虑我对概念措辞的直观反应。首先引用您对 CopyConstructible 的(非常好的)答案:“我可以从 T 构造一个 T”。这就是 Constructible 的用途吗?其次...... CopyConstructible 是“我可以从 T 中复制一个 T”会更好吗?
  • @hoo2:像这样的要求更多是关于表达式的可行性而不是语义效果(尽管它们也有)。考虑T t = some_t;T t = func_returning_t();。用户是否在乎它是否是“COPY”?还是他们只是关心他们有一个T 并能够从中构造另一个T
  • @hoo2:是的,用户关心类型是否为 move only,因此 T t = some_t; 不会编译。但是为什么用户希望T t = func_returning_t(); 不编译呢?如果可以从左值创建实例,为什么不能从右值创建实例?可复制但不可移动的类型会破坏这些操作的基本基本概念。
  • @hoo2 “允许移动但不能复制”是一个完全合理的语义,其中不乏重要的用例。 “允许复制但不能移动”是……字面上可以写的东西。这些并不完全平等。
  • @tangy 那句话上面写着“(直到 C++11)”。
猜你喜欢
  • 2016-05-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-08
  • 2017-09-18
  • 2016-07-10
  • 2021-06-16
  • 1970-01-01
相关资源
最近更新 更多