diff --git a/libcxx/include/functional b/libcxx/include/functional --- a/libcxx/include/functional +++ b/libcxx/include/functional @@ -216,6 +216,8 @@ template // deprecated in C++17 binary_negate not2(const Predicate& pred); +struct identity; + template constexpr unspecified not_fn(F&& f); // C++17, constexpr in C++20 @@ -3231,6 +3233,19 @@ return __old_size - __c.size(); } +#if _LIBCPP_STD_VER > 17 +// [func.identity] +struct identity { + template + constexpr _Tp&& operator()(_Tp&& __t) const noexcept + { + return _VSTD::forward<_Tp>(__t); + } + + using is_transparent = void; +}; +#endif // _LIBCPP_STD_VER > 17 + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP_FUNCTIONAL diff --git a/libcxx/test/std/utilities/function.objects/func.identity/identity.pass.cpp b/libcxx/test/std/utilities/function.objects/func.identity/identity.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/function.objects/func.identity/identity.pass.cpp @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// struct identity; + +#include + +#include +#include +#include +#include +#include + +constexpr auto id = std::identity(); +static_assert(requires { typename std::identity::is_transparent; }); + +template +constexpr std::remove_cvref_t const& as_const_lvalue(T&& t) noexcept { + return const_cast const&>( + static_cast const volatile&>(t)); +} + +template +constexpr bool check_identity_impl(U&& t) { + auto value = as_const_lvalue(t); + const bool result = + as_const_lvalue(id(std::forward(value))) == as_const_lvalue(t); + + if constexpr (!std::is_rvalue_reference_v) { + assert(&id(std::forward(value)) == + &value); // use after move irrelevant here + } + + return result; +} + +template +constexpr bool check_identity(T t) { + static_assert(requires(T u) { + { id(static_cast(u)) } -> std::same_as; + { id(static_cast(u)) } -> std::same_as; + { id(static_cast(u)) } -> std::same_as; + { id(static_cast(u)) } -> std::same_as; + { id(static_cast(u)) } -> std::same_as; + { id(static_cast(u)) } -> std::same_as; + { + id(static_cast(u)) + } -> std::same_as; + { + id(static_cast(u)) + } -> std::same_as; + }); + + assert(check_identity_impl(t)); + assert(check_identity_impl(t)); + assert(check_identity_impl(t)); + assert(check_identity_impl(t)); + + if (!std::is_constant_evaluated()) { + assert(check_identity_impl(t)); + assert(check_identity_impl(t)); + assert(check_identity_impl(t)); + assert(check_identity_impl(t)); + } + + return true; +} + +static_assert(check_identity(0)); +static_assert(check_identity(0.0)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); + +constexpr int x[] = {0, 1, 2, 3, 4}; +static_assert(check_identity(x)); + +struct S; +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); +static_assert(check_identity(nullptr)); + +struct S2 { + int member = 10; + void member_func() & {} + void member_func() && {} + void member_func() const& {} + void member_func() const&& {} + void member_func() volatile& {} + void member_func() volatile&& {} + void member_func() const volatile& {} + void member_func() const volatile&& {} + + [[noexcept]] constexpr bool operator==(S2 const&) const noexcept = default; +}; +static_assert(check_identity(S2())); + +int main(int, char**) { + assert(check_identity(0)); + assert(check_identity(0.0)); + assert(check_identity(nullptr)); + assert(check_identity(nullptr)); + + int x2[] = {0, 1, 2, 3, 4}; + assert(check_identity(x2)); + + S2 s2; + assert(check_identity(s2)); + assert(check_identity(&S2::member)); + assert(check_identity(&S2::member_func)); + assert(check_identity(&S2::member_func)); + assert(check_identity(&S2::member_func)); + assert(check_identity(&S2::member_func)); + assert(check_identity(&S2::member_func)); + assert(check_identity(&S2::member_func)); + assert(check_identity(&S2::member_func)); + assert(check_identity(&S2::member_func)); + + // something that's not trivially copyable + auto v = std::vector{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + assert(check_identity(v)); + + // something that's move-only + { + auto p = std::make_unique >(v); + assert(id(p) == p); + assert(&id(p) == &p); + + assert(id(std::as_const(p)) == p); + assert(&id(std::as_const(p)) == &p); + + assert(as_const_lvalue(id( + static_cast > volatile&>(p))) == + p); + assert(&id(static_cast > volatile&>(p)) == + &p); + + assert(as_const_lvalue(id( + static_cast > const volatile&>( + p))) == p); + assert(&id(static_cast > const volatile&>( + p)) == &p); + } + { + auto p = std::make_unique >(v); + assert(id(std::move(p)) == p); + } + { + auto p = std::make_unique >(v); + assert(id(std::move(std::as_const(p))) == p); + } + { + auto p = std::make_unique >(v); + assert(as_const_lvalue( + id(static_cast > volatile&&>( + p))) == p); + } + { + auto p = std::make_unique >(v); + assert(as_const_lvalue(id( + static_cast > const volatile&&>( + p))) == p); + } + + return 0; +}