diff --git a/libcxx/include/concepts b/libcxx/include/concepts --- a/libcxx/include/concepts +++ b/libcxx/include/concepts @@ -326,6 +326,14 @@ constructible_from<_Tp, const _Tp&> && convertible_to<const _Tp&, _Tp> && constructible_from<_Tp, const _Tp> && convertible_to<const _Tp, _Tp>; +// [concepts.object] +template<class _Tp> +concept movable = + is_object_v<_Tp> && + move_constructible<_Tp> && + assignable_from<_Tp&, _Tp> && + swappable<_Tp>; + // [concept.invocable] template<class _Fn, class... _Args> concept invocable = requires(_Fn&& __fn, _Args&&... __args) { diff --git a/libcxx/test/std/concepts/object/movable.compile.pass.cpp b/libcxx/test/std/concepts/object/movable.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/concepts/object/movable.compile.pass.cpp @@ -0,0 +1,180 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts + +// template<class From> +// concept movable = see below; + +#include <concepts> + +#include <deque> +#include <forward_list> +#include <list> +#include <map> +#include <mutex> +#include <optional> +#include <unordered_map> +#include <vector> + +// Movable types +static_assert(std::movable<int>); +static_assert(std::movable<int volatile>); +static_assert(std::movable<int*>); +static_assert(std::movable<int const*>); +static_assert(std::movable<int volatile*>); +static_assert(std::movable<int const volatile*>); +static_assert(std::movable<int (*)()>); + +struct S {}; +static_assert(std::movable<S>); +static_assert(std::movable<int S::*>); +static_assert(std::movable<int (S::*)()>); +static_assert(std::movable<int (S::*)() noexcept>); +static_assert(std::movable<int (S::*)() &>); +static_assert(std::movable<int (S::*)() & noexcept>); +static_assert(std::movable<int (S::*)() &&>); +static_assert(std::movable<int (S::*)() && noexcept>); +static_assert(std::movable<int (S::*)() const>); +static_assert(std::movable<int (S::*)() const noexcept>); +static_assert(std::movable<int (S::*)() const&>); +static_assert(std::movable<int (S::*)() const & noexcept>); +static_assert(std::movable<int (S::*)() const&&>); +static_assert(std::movable<int (S::*)() const && noexcept>); +static_assert(std::movable<int (S::*)() volatile>); +static_assert(std::movable<int (S::*)() volatile noexcept>); +static_assert(std::movable<int (S::*)() volatile&>); +static_assert(std::movable<int (S::*)() volatile & noexcept>); +static_assert(std::movable<int (S::*)() volatile&&>); +static_assert(std::movable<int (S::*)() volatile && noexcept>); +static_assert(std::movable<int (S::*)() const volatile>); +static_assert(std::movable<int (S::*)() const volatile noexcept>); +static_assert(std::movable<int (S::*)() const volatile&>); +static_assert(std::movable<int (S::*)() const volatile & noexcept>); +static_assert(std::movable<int (S::*)() const volatile&&>); +static_assert(std::movable<int (S::*)() const volatile && noexcept>); + +#ifndef _LIBCPP_HAS_NO_THREADS +using std::mutex; +#else +struct mutex { + mutex(mutex&&) = delete; + mutex& operator=(mutex&&) = delete; +}; +#endif + +static_assert(std::movable<std::deque<int> >); +static_assert(std::movable<std::forward_list<int> >); +static_assert(std::movable<std::list<int> >); +static_assert(std::movable<std::map<mutex, mutex> >); +static_assert(std::movable<std::optional<std::vector<int> > >); +static_assert(std::movable<std::unordered_map<mutex, mutex> >); +static_assert(std::movable<std::vector<int> >); +static_assert(std::movable<std::vector<mutex> >); + +struct traditional_copy_assignment_only { + traditional_copy_assignment_only& + operator=(traditional_copy_assignment_only const&); +}; +static_assert(std::is_move_assignable_v<traditional_copy_assignment_only>); +static_assert(std::movable<traditional_copy_assignment_only>); + +// Not objects +static_assert(!std::movable<int&>); +static_assert(!std::movable<int const&>); +static_assert(!std::movable<int volatile&>); +static_assert(!std::movable<int const volatile&>); +static_assert(!std::movable<int&&>); +static_assert(!std::movable<int const&&>); +static_assert(!std::movable<int volatile&&>); +static_assert(!std::movable<int const volatile&&>); +static_assert(!std::movable<int()>); +static_assert(!std::movable<int (&)()>); +static_assert(!std::movable<int[5]>); + +// Not move constructible or move assignable +static_assert(!std::movable<mutex>); +static_assert(!std::movable<std::optional<mutex> >); + +// T not move constructible +struct no_move_constructor { + no_move_constructor(no_move_constructor&&) = delete; +}; +static_assert(!std::movable<no_move_constructor>); + +// T& not assignable from T +static_assert(!std::movable<int const>); +static_assert(!std::movable<int const volatile>); + +struct no_move_assignment { + no_move_assignment& operator=(no_move_assignment&&) = delete; +}; +static_assert(!std::movable<no_move_assignment>); + +struct mutable_copy_assignment { + mutable_copy_assignment& operator=(mutable_copy_assignment&); +}; +static_assert( + !std::assignable_from<mutable_copy_assignment&, mutable_copy_assignment>); +static_assert(!std::movable<mutable_copy_assignment>); + +struct const_move_constructor { + const_move_constructor& operator=(const_move_constructor&&) const; +}; +static_assert( + !std::assignable_from<const_move_constructor&, const_move_constructor>); +static_assert(!std::movable<const_move_constructor>); + +struct derived_from_nonmovable : mutex {}; +static_assert(!std::movable<derived_from_nonmovable>); + +struct has_a_nonmovable { + mutex m; +}; +static_assert(!std::movable<has_a_nonmovable>); + +struct has_const_member { + int const x; +}; +static_assert(!std::movable<has_const_member>); + +struct has_volatile_member { + int volatile x; +}; +static_assert(std::movable<has_volatile_member>); + +struct has_cv_member { + int const volatile x; +}; +static_assert(!std::movable<has_cv_member>); + +struct has_lvalue_reference_member { + int& x; +}; +static_assert(!std::movable<has_lvalue_reference_member>); + +struct has_rvalue_reference_member { + int&& x; +}; +static_assert(!std::movable<has_rvalue_reference_member>); + +struct has_array_member { + int x[5]; +}; +static_assert(std::movable<has_array_member>); + +struct has_function_ref_member { + int (&f)(); +}; +static_assert(!std::movable<has_function_ref_member>); + +// `move_constructible and assignable_from<T&, T>` implies `swappable<T>`, +// so there's nothing to test for the case of non-swappable. + +int main(int, char**) { return 0; }