为什么CopyConstructible类型也必须是MoveConstructible?

hoo*_*oo2 10 c++ c++-concepts c++20

cppreference中所述,类型TCopyConstructible的要求是它也是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>;
Run Code Online (Sandbox Code Playgroud)

它支持命名的需求声明.鉴于上述定义,类似于:

struct HaveCopy {
   HaveCopy(const HaveCopy&)  = default;
   HaveCopy(HaveCopy&&)       = delete;
   HaveCopy& operator= (const HaveCopy&)  = default;
   HaveCopy& operator= (HaveCopy&&)       = delete;
};
Run Code Online (Sandbox Code Playgroud)

没有简单的测试:

static_assert(std::CopyConstructible<HaveCopy>);
Run Code Online (Sandbox Code Playgroud)

而它通过了旧的:

static_assert(std::is_copy_constructible<HaveCopy>::value);
Run Code Online (Sandbox Code Playgroud)

那么,问题是为什么呢?标准委员会对此事的意图是什么?HaveCopy不是可构造的,但在我看来几乎是可复制的,并且std::is_copy_constructible<>与我一致.

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

template <class T>
concept Copyable =
   std::CopyConstructible<T> &&
   std::Movable<T> &&
   std::Assignable<T&, const T&>;
Run Code Online (Sandbox Code Playgroud)

所以测试:

static_assert(std::Copyable<HaveCopy>);
Run Code Online (Sandbox Code Playgroud)

也会失败.这次失败加倍.双方CopyConstrucible<>Movable<>同意该HaveCopy是可复制.

这里的讨论有点类似,但没有回答原因.为什么我们需要这种行为?这种检查是否排除了有效的可复制构造类型,或者HaveCopy是否完全不可复制?如果确实如此,那对我来说似乎很奇怪.

有什么想法吗?

Bar*_*rry 7

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

struct A {
    A(A&) = delete;
    A(A const&);
};
Run Code Online (Sandbox Code Playgroud)

这个怎么样?

struct B {
    explicit B(B const&);
};
Run Code Online (Sandbox Code Playgroud)

关键是,你可以编写各种各样的构造函数组合.这并不意味着他们都有意义或值得支持.具有复制构造函数但删除的移动构造函数的类型根本没有意义.

概念不只是进行语法检查,它还涉及强制语义需求以推送有意义的类型 - 这最终会更容易编码.如果您只是检查is_copy_constructible,那么您允许自己做的就是从const左值显式构造您的类型.写作T x = y;,即使y是一个const T,已经超出了这个范围!这可能是字面上拷贝构造的手段,但它比少了很多有意义的更广泛的"我可以构建一个TT" -这是很多更接近真正的我们认为,当我们考虑复制.这就是这个概念CopyConstructible给我们带来的.

当您浏览库时,还有其他概念需要更多(语法和语义),而不是直接翻译它们的名称.EqualityComparableWith<T,U>不只是检查我可以写t == u,而且u == t,t != uu != t也.StrictTotallyOrdered不只是检查订货的运营商,它检查==.拥有一个有凝聚力的整体很重要.

  • @ hoo2:是的,用户关心的是类型是否只是**,因此`t t = some_t;`不编译.但是为什么用户希望`T t = func_returning_t();`不能编译?如果您可以从左值创建实例,为什么不能从右值创建一个实例?可复制但不可移动的类型是打破这些操作的基本概念的类型. (2认同)
  • @ hoo2"允许移动但不复制"是一个非常合理的语义,不缺少重要的用例."允许复制但不移动"是......字面上可能写的东西.这些并非完全平等. (2认同)