[libc++] Implement std::function_ref (#186692)
Papers: - P0792R14: function_ref: a type-erased callable reference - P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments - P3961R1 Less double indirection in function_ref (RU-220) LWG issues: - LWG4256: Incorrect constraints for function_ref constructors from nontype_t - LWG4425: CTAD function_ref of data member pointer should produce noexcept signature Fixes #189606 Fixes #105376 Fixes #189604 Fixes #171321 Fixes #171387 --------- Co-authored-by: Xiaoyang Liu <siujoeng.lau@gmail.com> Co-authored-by: Louis Dionne <ldionne.2@gmail.com>
This commit is contained in:
parent
5bc304c654
commit
a434fe87cc
32 changed files with 3356 additions and 32 deletions
|
|
@ -480,7 +480,7 @@ Status
|
|||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_fstream_native_handle`` ``202306L``
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_function_ref`` *unimplemented*
|
||||
``__cpp_lib_function_ref`` ``202603L``
|
||||
---------------------------------------------------------- -----------------
|
||||
``__cpp_lib_generate_random`` *unimplemented*
|
||||
---------------------------------------------------------- -----------------
|
||||
|
|
|
|||
|
|
@ -39,6 +39,9 @@ Implemented Papers
|
|||
------------------
|
||||
|
||||
- P0493R5: Atomic minimum/maximum (`Github <https://llvm.org/PR105418>`__)
|
||||
- P0792R14: ``function_ref`` : a type-erased callable reference (`Github <https://llvm.org/PR105376>`__)
|
||||
- P3948R1: ``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments (`Github <https://llvm.org/PR189604>`__)
|
||||
- P3961R1: Less double indirection in ``function_ref`` (RU-220) (`Github <https://llvm.org/PR189606>`__)
|
||||
|
||||
Improvements and New Features
|
||||
-----------------------------
|
||||
|
|
|
|||
|
|
@ -161,7 +161,7 @@
|
|||
"`LWG4251 <https://wg21.link/LWG4251>`__","Move assignment for ``indirect`` unnecessarily requires copy construction","2025-11 (Kona)","","","`#171318 <https://github.com/llvm/llvm-project/issues/171318>`__",""
|
||||
"`LWG4253 <https://wg21.link/LWG4253>`__","``basic_const_iterator`` should provide ``iterator_type``","2025-11 (Kona)","","","`#171319 <https://github.com/llvm/llvm-project/issues/171319>`__",""
|
||||
"`LWG4255 <https://wg21.link/LWG4255>`__","``move_only_function`` constructor should recognize empty ``copyable_function``\s","2025-11 (Kona)","","","`#171320 <https://github.com/llvm/llvm-project/issues/171320>`__",""
|
||||
"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","","","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
|
||||
"`LWG4256 <https://wg21.link/LWG4256>`__","Incorrect constrains for ``function_ref`` constructors from ``nontype_t``","2025-11 (Kona)","|Complete|","24","`#171321 <https://github.com/llvm/llvm-project/issues/171321>`__",""
|
||||
"`LWG4257 <https://wg21.link/LWG4257>`__","Stream insertion for ``chrono::local_time`` should be constrained","2025-11 (Kona)","","","`#171322 <https://github.com/llvm/llvm-project/issues/171322>`__",""
|
||||
"`LWG4260 <https://wg21.link/LWG4260>`__","Query objects must be default constructible","2025-11 (Kona)","","","`#171323 <https://github.com/llvm/llvm-project/issues/171323>`__",""
|
||||
"`LWG4265 <https://wg21.link/LWG4265>`__","``std::midpoint`` should not accept ``const bool``","2025-11 (Kona)","|Complete|","22","`#171324 <https://github.com/llvm/llvm-project/issues/171324>`__",""
|
||||
|
|
@ -225,7 +225,7 @@
|
|||
"`LWG4422 <https://wg21.link/LWG4422>`__","``meta::access_context`` should be a consteval-only type","2025-11 (Kona)","","","`#171384 <https://github.com/llvm/llvm-project/issues/171384>`__",""
|
||||
"`LWG4423 <https://wg21.link/LWG4423>`__","``meta::data_member_spec`` allows negative bit-field widths","2025-11 (Kona)","","","`#171385 <https://github.com/llvm/llvm-project/issues/171385>`__",""
|
||||
"`LWG4424 <https://wg21.link/LWG4424>`__","``meta::define_aggregate`` should require a class type","2025-11 (Kona)","","","`#171386 <https://github.com/llvm/llvm-project/issues/171386>`__",""
|
||||
"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","","","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
|
||||
"`LWG4425 <https://wg21.link/LWG4425>`__","CTAD ``function_ref`` of data member pointer should produce noexcept signature","2025-11 (Kona)","|Complete|","24","`#171387 <https://github.com/llvm/llvm-project/issues/171387>`__",""
|
||||
"`LWG4426 <https://wg21.link/LWG4426>`__","Clarify what ``meta::reflect_constant_string`` considers a string literal","2025-11 (Kona)","","","`#171388 <https://github.com/llvm/llvm-project/issues/171388>`__",""
|
||||
"`LWG4427 <https://wg21.link/LWG4427>`__","``meta::dealias`` needs to work with things that aren't entities","2025-11 (Kona)","","","`#171390 <https://github.com/llvm/llvm-project/issues/171390>`__",""
|
||||
"`LWG4428 <https://wg21.link/LWG4428>`__","Metafunctions should not be defined in terms of constant subexpressions","2025-11 (Kona)","","","`#171391 <https://github.com/llvm/llvm-project/issues/171391>`__",""
|
||||
|
|
|
|||
|
|
|
@ -14,7 +14,7 @@
|
|||
"`P2363R5 <https://wg21.link/P2363R5>`__","Extending associative containers with the remaining heterogeneous overloads","2023-06 (Varna)","","","`#105371 <https://github.com/llvm/llvm-project/issues/105371>`__",""
|
||||
"`P1901R2 <https://wg21.link/P1901R2>`__","Enabling the Use of ``weak_ptr`` as Keys in Unordered Associative Containers","2023-06 (Varna)","","","`#105372 <https://github.com/llvm/llvm-project/issues/105372>`__",""
|
||||
"`P1885R12 <https://wg21.link/P1885R12>`__","Naming Text Encodings to Demystify Them","2023-06 (Varna)","|Complete|","23","`#105373 <https://github.com/llvm/llvm-project/issues/105373>`__",""
|
||||
"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","","","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
|
||||
"`P0792R14 <https://wg21.link/P0792R14>`__","``function_ref``: a type-erased callable reference","2023-06 (Varna)","|Complete|","24","`#105376 <https://github.com/llvm/llvm-project/issues/105376>`__",""
|
||||
"`P2874R2 <https://wg21.link/P2874R2>`__","P2874R2: Mandating Annex D Require No More","2023-06 (Varna)","|Complete|","12","`#105377 <https://github.com/llvm/llvm-project/issues/105377>`__",""
|
||||
"`P2757R3 <https://wg21.link/P2757R3>`__","Type-checking format args","2023-06 (Varna)","","","`#105378 <https://github.com/llvm/llvm-project/issues/105378>`__",""
|
||||
"`P2637R3 <https://wg21.link/P2637R3>`__","Member ``visit``","2023-06 (Varna)","|Complete|","19","`#105380 <https://github.com/llvm/llvm-project/issues/105380>`__","Change of ``__cpp_lib_variant`` is completed in LLVM 20. Change of ``__cpp_lib_format`` is blocked by `P2419R2 <https://wg21.link/P2419R2>`__."
|
||||
|
|
@ -192,9 +192,9 @@
|
|||
"`P3725R3 <https://wg21.link/P3725R3>`__","Filter View Extensions for Safer Use, Rev 3","2026-03 (Croydon)","","","`#189601 <https://github.com/llvm/llvm-project/issues/189601>`__",""
|
||||
"`P3828R1 <https://wg21.link/P3828R1>`__","Rename the to_input view to as_input","2026-03 (Croydon)","","","`#189602 <https://github.com/llvm/llvm-project/issues/189602>`__",""
|
||||
"`P3795R2 <https://wg21.link/P3795R2>`__","Miscellaneous Reflection Cleanup","2026-03 (Croydon)","","","`#189603 <https://github.com/llvm/llvm-project/issues/189603>`__",""
|
||||
"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","","","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
|
||||
"`P3948R1 <https://wg21.link/P3948R1>`__","``constant_wrapper`` is the only tool needed for passing constant expressions via function arguments","2026-03 (Croydon)","|Complete|","24","`#189604 <https://github.com/llvm/llvm-project/issues/189604>`__",""
|
||||
"`P3978R3 <https://wg21.link/P3978R3>`__","``constant_wrapper`` should unwrap on call and subscript","2026-03 (Croydon)","|Complete|","23","`#189605 <https://github.com/llvm/llvm-project/issues/189605>`__",""
|
||||
"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","","","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
|
||||
"`P3961R1 <https://wg21.link/P3961R1>`__","Less double indirection in ``function_ref`` (RU-220)","2026-03 (Croydon)","|Complete|","24","`#189606 <https://github.com/llvm/llvm-project/issues/189606>`__",""
|
||||
"`P3981R2 <https://wg21.link/P3981R2>`__","Better return types in ``std::inplace_vector`` and ``std::exception_ptr_cast``","2026-03 (Croydon)","","","`#189607 <https://github.com/llvm/llvm-project/issues/189607>`__",""
|
||||
"`P4022R0 <https://wg21.link/P4022R0>`__","Remove ``try_append_range`` from ``inplace_vector`` for now","2026-03 (Croydon)","","","`#189608 <https://github.com/llvm/llvm-project/issues/189608>`__",""
|
||||
"`P4037R1 <https://wg21.link/P4037R1>`__","Supporting ``signed char`` and ``unsigned char`` in random number generation","2026-03 (Croydon)","","","`#189609 <https://github.com/llvm/llvm-project/issues/189609>`__",""
|
||||
|
|
|
|||
|
|
|
@ -438,6 +438,9 @@ set(files
|
|||
__functional/compose.h
|
||||
__functional/default_searcher.h
|
||||
__functional/function.h
|
||||
__functional/function_ref.h
|
||||
__functional/function_ref_common.h
|
||||
__functional/function_ref_impl.h
|
||||
__functional/hash.h
|
||||
__functional/identity.h
|
||||
__functional/invoke.h
|
||||
|
|
@ -911,6 +914,7 @@ set(files
|
|||
__type_traits/rank.h
|
||||
__type_traits/reference_constructs_from_temporary.h
|
||||
__type_traits/reference_converts_from_temporary.h
|
||||
__type_traits/register_passable.h
|
||||
__type_traits/remove_all_extents.h
|
||||
__type_traits/remove_const.h
|
||||
__type_traits/remove_const_ref.h
|
||||
|
|
|
|||
33
libcxx/include/__functional/function_ref.h
Normal file
33
libcxx/include/__functional/function_ref.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
|
||||
#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
|
||||
|
||||
#include <__config>
|
||||
#include <__functional/function_ref_common.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#if _LIBCPP_STD_VER >= 26
|
||||
// NOLINTBEGIN(readability-duplicate-include)
|
||||
|
||||
# define _LIBCPP_FUNCTION_REF_CV
|
||||
# include <__functional/function_ref_impl.h>
|
||||
# undef _LIBCPP_FUNCTION_REF_CV
|
||||
|
||||
# define _LIBCPP_FUNCTION_REF_CV const
|
||||
# include <__functional/function_ref_impl.h>
|
||||
# undef _LIBCPP_FUNCTION_REF_CV
|
||||
|
||||
// NOLINTEND(readability-duplicate-include)
|
||||
#endif // _LIBCPP_STD_VER >= 26
|
||||
|
||||
#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
|
||||
108
libcxx/include/__functional/function_ref_common.h
Normal file
108
libcxx/include/__functional/function_ref_common.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
|
||||
#define _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
|
||||
|
||||
#include <__config>
|
||||
#include <__type_traits/invoke.h>
|
||||
#include <__type_traits/is_const.h>
|
||||
#include <__type_traits/is_function.h>
|
||||
#include <__type_traits/is_object.h>
|
||||
#include <__type_traits/is_reference.h>
|
||||
#include <__type_traits/is_trivially_constructible.h>
|
||||
#include <__type_traits/is_trivially_destructible.h>
|
||||
#include <__type_traits/remove_cv.h>
|
||||
#include <__type_traits/remove_pointer.h>
|
||||
#include <__utility/constant_wrapper.h>
|
||||
#include <__utility/declval.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER >= 26
|
||||
|
||||
template <class...>
|
||||
class function_ref;
|
||||
|
||||
template <class _Fn1, class _Fn2>
|
||||
struct __is_convertible_from_specialization : false_type {};
|
||||
|
||||
union __function_ref_storage {
|
||||
void* __obj_ptr_;
|
||||
void (*__fn_ptr_)();
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage() noexcept : __obj_ptr_(nullptr) {}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr explicit __function_ref_storage(_Tp* __ptr) noexcept {
|
||||
if constexpr (is_object_v<_Tp>) {
|
||||
__obj_ptr_ = const_cast<remove_cv_t<_Tp>*>(__ptr);
|
||||
} else {
|
||||
static_assert(is_function_v<_Tp>);
|
||||
__fn_ptr_ = reinterpret_cast<void (*)()>(__ptr);
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
_LIBCPP_HIDE_FROM_ABI static constexpr auto __get(__function_ref_storage __storage) {
|
||||
if constexpr (is_object_v<_Tp>) {
|
||||
return static_cast<_Tp*>(__storage.__obj_ptr_);
|
||||
} else {
|
||||
static_assert(is_function_v<_Tp>);
|
||||
return reinterpret_cast<_Tp*>(__storage.__fn_ptr_);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Fp, class _Tp>
|
||||
struct __function_ref_bind {};
|
||||
|
||||
// F is of the form R(*)(G, A...) noexcept(E) for a type G.
|
||||
template <bool _Noexcept, class _Tp, class _Rp, class _Gp, class... _ArgTypes>
|
||||
struct __function_ref_bind<_Rp (*)(_Gp, _ArgTypes...) noexcept(_Noexcept), _Tp> {
|
||||
using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(_Noexcept);
|
||||
};
|
||||
|
||||
template <class _Tp, class _Mp, class _Gp>
|
||||
requires is_object_v<_Mp>
|
||||
struct __function_ref_bind<_Mp _Gp::*, _Tp> {
|
||||
using type _LIBCPP_NODEBUG = invoke_result_t<_Mp _Gp::*, _Tp&>() noexcept;
|
||||
};
|
||||
|
||||
template <class _Fp, class _Tp>
|
||||
using __function_ref_bind_t _LIBCPP_NODEBUG = __function_ref_bind<_Fp, _Tp>::type;
|
||||
|
||||
template <class _Fp>
|
||||
requires is_function_v<_Fp>
|
||||
function_ref(_Fp*) -> function_ref<_Fp>;
|
||||
|
||||
template <auto _Cw, class _Fn>
|
||||
requires is_function_v<remove_pointer_t<_Fn>>
|
||||
function_ref(constant_wrapper<_Cw, _Fn>) -> function_ref<remove_pointer_t<_Fn>>;
|
||||
|
||||
template <auto _Cw, class _Fn, class _Tp>
|
||||
function_ref(constant_wrapper<_Cw, _Fn>, _Tp&&) -> function_ref<__function_ref_bind_t<_Fn, _Tp&>>;
|
||||
|
||||
template <class>
|
||||
constexpr bool __is_constant_wrapper = false;
|
||||
|
||||
template <auto _Value>
|
||||
constexpr bool __is_constant_wrapper<constant_wrapper<_Value>> = true;
|
||||
|
||||
template <class _Fn, class... _Args>
|
||||
concept __statically_callable = requires { _Fn::operator()(std::declval<_Args>()...); };
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 26
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___FUNCTIONAL_FUNCTION_REF_COMMON_H
|
||||
201
libcxx/include/__functional/function_ref_impl.h
Normal file
201
libcxx/include/__functional/function_ref_impl.h
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// This header is unguarded on purpose. This header is an implementation detail of function_ref.h
|
||||
// and generates multiple versions of std::function_ref
|
||||
|
||||
#include <__assert>
|
||||
#include <__config>
|
||||
#include <__functional/function_ref_common.h>
|
||||
#include <__functional/invoke.h>
|
||||
#include <__memory/addressof.h>
|
||||
#include <__type_traits/conditional.h>
|
||||
#include <__type_traits/conjunction.h>
|
||||
#include <__type_traits/invoke.h>
|
||||
#include <__type_traits/is_const.h>
|
||||
#include <__type_traits/is_convertible.h>
|
||||
#include <__type_traits/is_function.h>
|
||||
#include <__type_traits/is_member_pointer.h>
|
||||
#include <__type_traits/is_object.h>
|
||||
#include <__type_traits/is_pointer.h>
|
||||
#include <__type_traits/is_reference.h>
|
||||
#include <__type_traits/is_same.h>
|
||||
#include <__type_traits/is_void.h>
|
||||
#include <__type_traits/register_passable.h>
|
||||
#include <__type_traits/remove_cv.h>
|
||||
#include <__type_traits/remove_cvref.h>
|
||||
#include <__type_traits/remove_pointer.h>
|
||||
#include <__type_traits/remove_reference.h>
|
||||
#include <__utility/constant_wrapper.h>
|
||||
#include <__utility/forward.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#ifndef _LIBCPP___FUNCTIONAL_FUNCTION_REF_H
|
||||
# error This header should only be included from function_ref.h
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER >= 26
|
||||
|
||||
template <class...>
|
||||
class function_ref;
|
||||
|
||||
template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
|
||||
struct __is_convertible_from_specialization<
|
||||
function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
|
||||
function_ref<_Rp(_ArgTypes...) const noexcept(_NoExcept2)> >
|
||||
: is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)> {};
|
||||
|
||||
template <bool _NoExcept1, bool _NoExcept2, class _Rp, class... _ArgTypes>
|
||||
struct __is_convertible_from_specialization<
|
||||
function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(_NoExcept1)>,
|
||||
function_ref<_Rp(_ArgTypes...) noexcept(_NoExcept2)> >
|
||||
: _And<is_convertible<_Rp (&)(_ArgTypes...) noexcept(_NoExcept2), _Rp (&)(_ArgTypes...) noexcept(_NoExcept1)>,
|
||||
is_convertible<_LIBCPP_FUNCTION_REF_CV int&, int&>> {};
|
||||
|
||||
template <class _Rp, class... _ArgTypes, bool __is_noexcept>
|
||||
class function_ref<_Rp(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept)> {
|
||||
private:
|
||||
template <class... _Tp>
|
||||
static constexpr bool __is_invocable_using =
|
||||
_If<__is_noexcept, is_nothrow_invocable_r<_Rp, _Tp..., _ArgTypes...>, is_invocable_r<_Rp, _Tp..., _ArgTypes...>>::
|
||||
value;
|
||||
|
||||
template <class _Fn2>
|
||||
static constexpr bool __is_convertible_from_specialization_v =
|
||||
__is_convertible_from_specialization<function_ref, _Fn2>::value;
|
||||
|
||||
template <class... _Tp>
|
||||
friend class function_ref;
|
||||
|
||||
template <class _Arg>
|
||||
using __arg_t _LIBCPP_NODEBUG = _If<__register_passable<_Arg>, _Arg, _Arg&&>;
|
||||
|
||||
using __storage_t _LIBCPP_NODEBUG = __function_ref_storage;
|
||||
|
||||
using __call_t _LIBCPP_NODEBUG = _Rp (*)(__storage_t, __arg_t<_ArgTypes>...) noexcept(__is_noexcept);
|
||||
|
||||
__storage_t __storage_;
|
||||
__call_t __call_;
|
||||
|
||||
public:
|
||||
template <class _Fp>
|
||||
requires is_function_v<_Fp> && __is_invocable_using<_Fp>
|
||||
_LIBCPP_HIDE_FROM_ABI function_ref(_Fp* __fn_ptr) noexcept
|
||||
: __storage_(__fn_ptr),
|
||||
__call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
|
||||
_Fp* __fn_ptr1 = __storage_t::template __get<_Fp>(__storage);
|
||||
return std::invoke_r<_Rp>(__fn_ptr1, std::forward<__arg_t<_ArgTypes>>(__args)...);
|
||||
}) {
|
||||
_LIBCPP_ASSERT_NON_NULL(__fn_ptr != nullptr, "the function pointer should not be a nullptr");
|
||||
}
|
||||
|
||||
template <class _Fn, class _Tp = remove_reference_t<_Fn>>
|
||||
requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
|
||||
__is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && !__is_convertible_from_specialization_v<_Tp>)
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept {
|
||||
using _Dn = remove_cv_t<_Tp>;
|
||||
if constexpr (__statically_callable<_Dn, __arg_t<_ArgTypes>...>) {
|
||||
__call_ = [](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
|
||||
return _Dn::operator()(std::forward<__arg_t<_ArgTypes>>(__args)...);
|
||||
};
|
||||
} else {
|
||||
__storage_ = __storage_t(std::addressof(__obj)),
|
||||
__call_ = [](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
|
||||
_LIBCPP_FUNCTION_REF_CV _Tp& __obj1 = *__storage_t::template __get<_Tp>(__storage);
|
||||
return std::invoke_r<_Rp>(__obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template <class _Fn, class _Tp = remove_reference_t<_Fn>>
|
||||
requires(!is_same_v<remove_cvref_t<_Fn>, function_ref> && !is_member_pointer_v<_Tp> &&
|
||||
__is_invocable_using<_LIBCPP_FUNCTION_REF_CV _Tp&> && __is_convertible_from_specialization_v<_Tp>)
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(_Fn&& __obj) noexcept
|
||||
: __storage_(__obj.__storage_), __call_(__obj.__call_) {}
|
||||
|
||||
template <auto _Cw, class _Fn>
|
||||
requires __is_invocable_using<const _Fn&>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f) noexcept
|
||||
: __call_([](__storage_t, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
|
||||
return std::invoke_r<_Rp>(decltype(__f)::value, std::forward<__arg_t<_ArgTypes>>(__args)...);
|
||||
}) {
|
||||
if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
|
||||
static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
|
||||
}
|
||||
if constexpr (sizeof...(_ArgTypes) > 0 && (__constexpr_param<remove_cvref_t<_ArgTypes>> && ...)) {
|
||||
static_assert(
|
||||
!requires {
|
||||
typename constant_wrapper<std::invoke(decltype(__f)::value, remove_cvref_t<_ArgTypes>::value...)>;
|
||||
},
|
||||
"cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref "
|
||||
"constructed from cw would be ambiguous");
|
||||
}
|
||||
}
|
||||
|
||||
template <auto _Cw, class _Fn, class _Up, class _Tp = remove_reference_t<_Up>>
|
||||
requires(!is_rvalue_reference_v<_Up &&>) && __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp&>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f, _Up&& __obj) noexcept
|
||||
: __storage_(std::addressof(__obj)),
|
||||
__call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
|
||||
auto& __obj1 = static_cast<_LIBCPP_FUNCTION_REF_CV _Tp&>(*__storage_t::template __get<_Tp>(__storage));
|
||||
return std::invoke_r<_Rp>(decltype(__f)::value, __obj1, std::forward<__arg_t<_ArgTypes>>(__args)...);
|
||||
}) {
|
||||
if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
|
||||
static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
|
||||
}
|
||||
}
|
||||
|
||||
template <auto _Cw, class _Fn, class _Tp>
|
||||
requires __is_invocable_using<const _Fn&, _LIBCPP_FUNCTION_REF_CV _Tp*>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(constant_wrapper<_Cw, _Fn> __f,
|
||||
_LIBCPP_FUNCTION_REF_CV _Tp* __obj_ptr) noexcept
|
||||
: __storage_(__obj_ptr),
|
||||
__call_([](__storage_t __storage, __arg_t<_ArgTypes>... __args) static noexcept(__is_noexcept) -> _Rp {
|
||||
auto* __obj = __storage_t::template __get<_LIBCPP_FUNCTION_REF_CV _Tp>(__storage);
|
||||
return std::invoke_r<_Rp>(decltype(__f)::value, __obj, std::forward<__arg_t<_ArgTypes>>(__args)...);
|
||||
}) {
|
||||
if constexpr (is_pointer_v<_Fn> || is_member_pointer_v<_Fn>) {
|
||||
static_assert(__f.value != nullptr, "the function pointer should not be a nullptr");
|
||||
}
|
||||
|
||||
if constexpr (is_member_pointer_v<_Fn>) {
|
||||
_LIBCPP_ASSERT_NON_NULL(__obj_ptr != nullptr, "the object pointer should not be a nullptr");
|
||||
}
|
||||
}
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr function_ref(const function_ref&) noexcept = default;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr function_ref& operator=(const function_ref&) noexcept = default;
|
||||
|
||||
template <class _Tp>
|
||||
requires(!__is_convertible_from_specialization_v<_Tp>) && (!is_pointer_v<_Tp>) && (!__is_constant_wrapper<_Tp>)
|
||||
_LIBCPP_HIDE_FROM_ABI function_ref& operator=(_Tp) = delete;
|
||||
|
||||
_LIBCPP_HIDE_FROM_ABI _Rp operator()(_ArgTypes... __args) const noexcept(__is_noexcept) {
|
||||
return __call_(__storage_, std::forward<_ArgTypes>(__args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
|
||||
struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV noexcept(__is_noexcept), _Tp> {
|
||||
using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
|
||||
};
|
||||
|
||||
template <class _Tp, class _Rp, class _Gp, class... _ArgTypes, bool __is_noexcept>
|
||||
struct __function_ref_bind<_Rp (_Gp::*)(_ArgTypes...) _LIBCPP_FUNCTION_REF_CV & noexcept(__is_noexcept), _Tp> {
|
||||
using type _LIBCPP_NODEBUG = _Rp(_ArgTypes...) noexcept(__is_noexcept);
|
||||
};
|
||||
|
||||
#endif // _LIBCPP_STD_VER >= 26
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
38
libcxx/include/__type_traits/register_passable.h
Normal file
38
libcxx/include/__type_traits/register_passable.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
|
||||
#define _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
|
||||
|
||||
#include <__config>
|
||||
#include <__type_traits/is_reference.h>
|
||||
#include <__type_traits/is_trivially_constructible.h>
|
||||
#include <__type_traits/is_trivially_destructible.h>
|
||||
|
||||
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# pragma GCC system_header
|
||||
#endif
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_STD
|
||||
|
||||
#if _LIBCPP_STD_VER >= 20
|
||||
|
||||
template <class _Arg>
|
||||
concept __itanium_trivial_for_calls =
|
||||
is_trivially_destructible_v<_Arg> && is_trivially_copy_constructible_v<_Arg> &&
|
||||
is_trivially_move_constructible_v<_Arg>;
|
||||
|
||||
template <class _Arg>
|
||||
concept __register_passable =
|
||||
!is_reference_v<_Arg> && sizeof(_Arg) <= 2 * sizeof(void*) && __itanium_trivial_for_calls<_Arg>;
|
||||
|
||||
#endif
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
||||
#endif // _LIBCPP___TYPE_TRAITS_REGISTER_PASSABLE_H
|
||||
|
|
@ -487,6 +487,11 @@ template <class R, class ... ArgTypes>
|
|||
template <class R, class ... ArgTypes>
|
||||
void swap(function<R(ArgTypes...)>&, function<R(ArgTypes...)>&) noexcept;
|
||||
|
||||
// [func.wrap.ref], non-owning wrapper
|
||||
template<class... S> class function_ref; // freestanding, not defined, since C++26
|
||||
template<class R, class... ArgTypes>
|
||||
class function_ref<R(ArgTypes...) cv noexcept(noex)>; // freestanding, since C++26
|
||||
|
||||
template <class T> struct hash;
|
||||
|
||||
template <> struct hash<bool>;
|
||||
|
|
@ -571,6 +576,10 @@ POLICY: For non-variadic implementations, the number of arguments is limited
|
|||
# include <__type_traits/unwrap_ref.h>
|
||||
# endif
|
||||
|
||||
# if _LIBCPP_STD_VER >= 26
|
||||
# include <__functional/function_ref.h>
|
||||
# endif
|
||||
|
||||
# include <version>
|
||||
|
||||
# if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@ module std_core {
|
|||
header "__type_traits/reference_converts_from_temporary.h"
|
||||
export std_core.type_traits.integral_constant
|
||||
}
|
||||
module register_passable { header "__type_traits/register_passable.h" }
|
||||
module remove_all_extents { header "__type_traits/remove_all_extents.h" }
|
||||
module remove_const_ref { header "__type_traits/remove_const_ref.h" }
|
||||
module remove_const { header "__type_traits/remove_const.h" }
|
||||
|
|
@ -1438,6 +1439,9 @@ module std {
|
|||
}
|
||||
module default_searcher { header "__functional/default_searcher.h" }
|
||||
module function { header "__functional/function.h" }
|
||||
module function_ref { header "__functional/function_ref.h" }
|
||||
module function_ref_common { header "__functional/function_ref_common.h" }
|
||||
module function_ref_impl { textual header "__functional/function_ref_impl.h" }
|
||||
module hash { header "__functional/hash.h" }
|
||||
module identity { header "__functional/identity.h" }
|
||||
module invoke { header "__functional/invoke.h" }
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ __cpp_lib_freestanding_optional 202311L <optional>
|
|||
__cpp_lib_freestanding_string_view 202311L <string_view>
|
||||
__cpp_lib_freestanding_variant 202311L <variant>
|
||||
__cpp_lib_fstream_native_handle 202306L <fstream>
|
||||
__cpp_lib_function_ref 202306L <functional>
|
||||
__cpp_lib_function_ref 202603L <functional>
|
||||
__cpp_lib_gcd_lcm 201606L <numeric>
|
||||
__cpp_lib_generate_random 202403L <random>
|
||||
__cpp_lib_generic_associative_lookup 201304L <map> <set>
|
||||
|
|
@ -593,7 +593,7 @@ __cpp_lib_void_t 201411L <type_traits>
|
|||
# if _LIBCPP_HAS_FILESYSTEM && _LIBCPP_HAS_LOCALIZATION
|
||||
# define __cpp_lib_fstream_native_handle 202306L
|
||||
# endif
|
||||
// # define __cpp_lib_function_ref 202306L
|
||||
# define __cpp_lib_function_ref 202603L
|
||||
// # define __cpp_lib_generate_random 202403L
|
||||
// # define __cpp_lib_hazard_pointer 202306L
|
||||
// # define __cpp_lib_inplace_vector 202406L
|
||||
|
|
|
|||
|
|
@ -95,6 +95,10 @@ export namespace std {
|
|||
|
||||
using std::function;
|
||||
|
||||
#if _LIBCPP_STD_VER >= 26
|
||||
using std::function_ref;
|
||||
#endif // _LIBCPP_STD_VER >= 26
|
||||
|
||||
using std::swap;
|
||||
|
||||
using std::operator==;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: has-unix-headers
|
||||
// REQUIRES: std-at-least-c++26
|
||||
// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
|
||||
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
|
||||
|
||||
// template<auto f, class T>
|
||||
// constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
|
||||
// Preconditions: If is_member_pointer_v<F> is true, obj is not a null pointer.
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "check_assertion.h"
|
||||
|
||||
struct A {
|
||||
void f() {}
|
||||
};
|
||||
|
||||
int main(int, char**) {
|
||||
TEST_LIBCPP_ASSERT_FAILURE(std::function_ref<void()>(std::cw<&A::f>, static_cast<A*>(nullptr)),
|
||||
"the object pointer should not be a nullptr");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: has-unix-headers
|
||||
// REQUIRES: std-at-least-c++26
|
||||
// UNSUPPORTED: libcpp-hardening-mode=none || libcpp-hardening-mode=fast
|
||||
// XFAIL: libcpp-hardening-mode=debug && availability-verbose_abort-missing
|
||||
|
||||
// template<class F> function_ref(F* f) noexcept;
|
||||
// Preconditions: f is not a null pointer.
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "check_assertion.h"
|
||||
|
||||
int main(int, char**) {
|
||||
TEST_LIBCPP_ASSERT_FAILURE(
|
||||
std::function_ref<void()>(static_cast<void (*)()>(nullptr)), "the function pointer should not be a nullptr");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// template<auto c, class F>
|
||||
// constexpr function_ref(constant_wrapper<c, F>) noexcept;
|
||||
// template<auto c, class F, class U>
|
||||
// constexpr function_ref(constant_wrapper<c, F>, U&& obj) noexcept;
|
||||
// template<auto c, class F, class T>
|
||||
// constexpr function_ref(constant_wrapper<c, F>, cv T* obj) noexcept;
|
||||
|
||||
// Mandates: If is_pointer_v<F> || is_member_pointer_v<F> is true, then f != nullptr is true.
|
||||
|
||||
// For the first overload, f ArgTypes is not an empty pack and all types in remove_cvref_t<ArgTypes>...
|
||||
// satisfy constexpr-param then constant_wrapper<INVOKE (f.value, remove_cvref_t<ArgTypes>::value...)>
|
||||
// is not a valid type.
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
struct A {
|
||||
void f();
|
||||
};
|
||||
|
||||
struct B {
|
||||
constexpr int operator()(std::constant_wrapper<42>) const { return 42; }
|
||||
constexpr int operator()(int) const { return 42; }
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
void test() {
|
||||
std::function_ref<void()> f1(std::cw<static_cast<void (*)()>(nullptr)>);
|
||||
// expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
|
||||
|
||||
std::function_ref<void(A)> f2(std::cw<static_cast<void (A::*)()>(nullptr)>);
|
||||
// expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
|
||||
|
||||
std::function_ref<void(std::constant_wrapper<42>)> f33(std::cw<B{}>);
|
||||
// expected-error-re@*:* {{static assertion failed {{.*}}cw(args...) should be equivalent to fn(args...), otherwise the intended behavior for a function_ref constructed from cw would be ambiguous}}
|
||||
|
||||
int i;
|
||||
std::function_ref<void()> f3(std::cw<static_cast<void (*)(int)>(nullptr)>, i);
|
||||
// expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
|
||||
|
||||
A a;
|
||||
std::function_ref<void()> f4(std::cw<static_cast<void (A::*)()>(nullptr)>, a);
|
||||
// expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
|
||||
|
||||
std::function_ref<void()> f5(std::cw<static_cast<void (*)(int*)>(nullptr)>, &i);
|
||||
// expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
|
||||
|
||||
std::function_ref<void()> f6(std::cw<static_cast<void (A::*)()>(nullptr)>, &a);
|
||||
// expected-error-re@*:* {{static assertion failed {{.*}}the function pointer should not be a nullptr}}
|
||||
}
|
||||
// clang-format on
|
||||
|
|
@ -494,17 +494,11 @@
|
|||
# endif
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_function_ref
|
||||
# error "__cpp_lib_function_ref should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_function_ref != 202306L
|
||||
# error "__cpp_lib_function_ref should have the value 202306L in c++26"
|
||||
# endif
|
||||
# else
|
||||
# ifdef __cpp_lib_function_ref
|
||||
# error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# ifndef __cpp_lib_function_ref
|
||||
# error "__cpp_lib_function_ref should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_function_ref != 202603L
|
||||
# error "__cpp_lib_function_ref should have the value 202603L in c++26"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_invoke
|
||||
|
|
|
|||
|
|
@ -7132,17 +7132,11 @@
|
|||
# endif
|
||||
# endif
|
||||
|
||||
# if !defined(_LIBCPP_VERSION)
|
||||
# ifndef __cpp_lib_function_ref
|
||||
# error "__cpp_lib_function_ref should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_function_ref != 202306L
|
||||
# error "__cpp_lib_function_ref should have the value 202306L in c++26"
|
||||
# endif
|
||||
# else
|
||||
# ifdef __cpp_lib_function_ref
|
||||
# error "__cpp_lib_function_ref should not be defined because it is unimplemented in libc++!"
|
||||
# endif
|
||||
# ifndef __cpp_lib_function_ref
|
||||
# error "__cpp_lib_function_ref should be defined in c++26"
|
||||
# endif
|
||||
# if __cpp_lib_function_ref != 202603L
|
||||
# error "__cpp_lib_function_ref should have the value 202603L in c++26"
|
||||
# endif
|
||||
|
||||
# ifndef __cpp_lib_gcd_lcm
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// template<class F>
|
||||
// function_ref(F*) -> function_ref<F>;
|
||||
//
|
||||
// template<auto c, class F0>
|
||||
// function_ref(constant_wrapper<c, F0>) -> function_ref<see below>;
|
||||
//
|
||||
// template<auto c, class F, class T>
|
||||
// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<see below>;
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
int fn(int, float) { return 42; }
|
||||
|
||||
int fn_noexcept(int, float) noexcept { return 42; }
|
||||
|
||||
struct S {
|
||||
int data_mem = 42;
|
||||
|
||||
int fn_mem(int, float) { return 42; }
|
||||
int fn_mem_ref(int, float) & { return 42; }
|
||||
int fn_mem_const(int, float) const { return 42; }
|
||||
int fn_mem_const_ref(int, float) const& { return 42; }
|
||||
int fn_mem_noexcept(int, float) noexcept { return 42; }
|
||||
};
|
||||
|
||||
void test() {
|
||||
// template<class F>
|
||||
// function_ref(F*) -> function_ref<F>;
|
||||
{
|
||||
std::function_ref fn_ref = fn;
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
|
||||
}
|
||||
{
|
||||
std::function_ref fn_ref = fn_noexcept;
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
|
||||
}
|
||||
|
||||
// template<auto c, class F>
|
||||
// function_ref(constant_wrapper<c, F>) -> function_ref<...>;
|
||||
{
|
||||
std::function_ref fn_ref = std::constant_wrapper<fn>();
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
|
||||
}
|
||||
{
|
||||
std::function_ref fn_ref = std::constant_wrapper<fn_noexcept>();
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
|
||||
}
|
||||
|
||||
// template<auto c, class F, class T>
|
||||
// function_ref(constant_wrapper<c, F>, T&&) -> function_ref<...>;
|
||||
{
|
||||
int arg = 0;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<fn>(), arg};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float)>>);
|
||||
}
|
||||
{
|
||||
int arg = 0;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<fn_noexcept>(), arg};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(float) noexcept>>);
|
||||
}
|
||||
{
|
||||
S s;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int&() noexcept>>);
|
||||
}
|
||||
{
|
||||
const S s;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<&S::data_mem>(), s};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int const&() noexcept>>);
|
||||
}
|
||||
{
|
||||
S s;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem>(), s};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
|
||||
}
|
||||
{
|
||||
S s;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const>(), s};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
|
||||
}
|
||||
{
|
||||
S s;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_ref>(), s};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
|
||||
}
|
||||
{
|
||||
S s;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_const_ref>(), s};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float)>>);
|
||||
}
|
||||
{
|
||||
S s;
|
||||
std::function_ref fn_ref = {std::constant_wrapper<&S::fn_mem_noexcept>(), s};
|
||||
static_assert(std::is_same_v<decltype(fn_ref), std::function_ref<int(int, float) noexcept>>);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// template<class T> function_ref& operator=(T) = delete;
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// Constraints:
|
||||
// - T is not the same type as function_ref,
|
||||
// - is_pointer_v<T> is false, and
|
||||
// - T is not a specialization of constant_wrapper.
|
||||
|
||||
// non const noexcept(false)
|
||||
static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void()>>);
|
||||
static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const>>);
|
||||
static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_assignable_v<std::function_ref<void()>, std::function_ref<void() const noexcept>>);
|
||||
|
||||
static_assert(std::is_assignable_v<std::function_ref<void()>, void (*)()>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void()>, void (*)(int)>);
|
||||
|
||||
static_assert(std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[] {}>>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void()>, std::constant_wrapper<[](int) {}>>);
|
||||
|
||||
// const noexcept(false)
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void()>>);
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const>>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() const>, std::function_ref<void() const noexcept>>);
|
||||
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() const>, void (*)()>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() const>, void (*)(int)>);
|
||||
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[] { return 42; }>>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() const>, std::constant_wrapper<[](int) { return 42; }>>);
|
||||
|
||||
// non-const noexcept(true)
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void()>>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const>>);
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::function_ref<void() const noexcept>>);
|
||||
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, void (*)() noexcept>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
|
||||
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[] noexcept {} >>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
|
||||
|
||||
// const noexcept(true)
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void()>>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const>>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::function_ref<void() const noexcept>>);
|
||||
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)() noexcept>);
|
||||
static_assert(!std::is_assignable_v<std::function_ref<void() const noexcept>, void (*)(int) noexcept>);
|
||||
|
||||
static_assert(std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[] noexcept {}>>);
|
||||
static_assert(
|
||||
!std::is_assignable_v<std::function_ref<void() const noexcept>, std::constant_wrapper<[](int) noexcept {}>>);
|
||||
|
||||
int forty_two() { return 42; }
|
||||
|
||||
// These runtime tests are testing that when the constraints are not met,
|
||||
// the assignment operator is not deleted and the implicit constructor
|
||||
// and the move assignment are called
|
||||
void test() {
|
||||
{
|
||||
std::function_ref<int()> f(std::cw<[] { return 41; }>);
|
||||
std::same_as<std::function_ref<int()>&> decltype(auto) result = f =
|
||||
std::function_ref<int()>(std::cw<[] { return 42; }>);
|
||||
assert(&result == &f);
|
||||
assert(f() == 42);
|
||||
}
|
||||
{
|
||||
std::function_ref<int() > f(std::cw<[] { return 41; }>);
|
||||
std::same_as<std::function_ref<int()>&> decltype(auto) result = f = &forty_two;
|
||||
assert(&result == &f);
|
||||
assert(f() == 42);
|
||||
}
|
||||
{
|
||||
std::function_ref<int() > f(std::cw<[] { return 41; }>);
|
||||
std::same_as<std::function_ref<int()>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
|
||||
assert(&result == &f);
|
||||
assert(f() == 42);
|
||||
}
|
||||
{
|
||||
// const
|
||||
std::function_ref<int() const> f(std::cw<[] { return 41; }>);
|
||||
std::same_as<std::function_ref<int() const>&> decltype(auto) result = f = std::cw<[] { return 42; }>;
|
||||
assert(&result == &f);
|
||||
assert(f() == 42);
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
std::function_ref<int() noexcept> f(std::cw<[] noexcept { return 41; }>);
|
||||
std::same_as<std::function_ref<int() noexcept>&> decltype(auto) result = f = std::cw<[] noexcept { return 42; }>;
|
||||
assert(&result == &f);
|
||||
assert(f() == 42);
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
std::function_ref<int() const noexcept> f(std::cw<[] noexcept { return 41; }>);
|
||||
std::same_as<std::function_ref<int() const noexcept>&> decltype(auto) result = f =
|
||||
std::cw<[] noexcept { return 42; }>;
|
||||
assert(&result == &f);
|
||||
assert(f() == 42);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// template<auto f> constexpr function_ref(constant_wrapper<f>) noexcept;
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// Constraints: is-invocable-using<const F&> is true.
|
||||
|
||||
auto l1 = [] {};
|
||||
auto l1_noexcept = [] noexcept {};
|
||||
auto l2 = [](int) {};
|
||||
auto l2_noexcept = [](int) noexcept {};
|
||||
|
||||
struct NonConstInvocable {
|
||||
void operator()() noexcept {}
|
||||
};
|
||||
|
||||
// non-const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
|
||||
// LWG issue 4256
|
||||
// https://cplusplus.github.io/LWG/issue4256
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
|
||||
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int)>, std::constant_wrapper<l2>>);
|
||||
|
||||
// non-const noexcept
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
|
||||
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>>);
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void(int) noexcept>, std::constant_wrapper<l2_noexcept>>);
|
||||
|
||||
// const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
|
||||
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int) const>, std::constant_wrapper<l2>>);
|
||||
|
||||
// const noexcept
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<NonConstInvocable{}>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
|
||||
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>>);
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void(int) const noexcept>, std::constant_wrapper<l2_noexcept>>);
|
||||
|
||||
double f1(int x, double y) noexcept { return x + y; }
|
||||
|
||||
struct Int {
|
||||
int i;
|
||||
constexpr Int(int ii) noexcept : i(ii) {}
|
||||
};
|
||||
|
||||
struct NeedsConversion {
|
||||
int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
|
||||
};
|
||||
|
||||
int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
std::function_ref<void()> f(std::cw<[] {}>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
{
|
||||
// explicit
|
||||
std::function_ref<void()> f = std::cw<[] {}>;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
std::function_ref<int() const> f(std::cw<[] { return 42; }>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions
|
||||
std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions function pointer
|
||||
std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,407 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// template<auto f, class T>
|
||||
// constexpr function_ref(constant_wrapper<f>, cv T* obj) noexcept;
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// Constraints: is-invocable-using<const F&, cv T*> is true.
|
||||
|
||||
auto l1 = [](int*) {};
|
||||
auto l1_const = [](const int*) {};
|
||||
auto l1_noexcept = [](int*) noexcept {};
|
||||
auto l1_const_noexcept = [](const int*) noexcept {};
|
||||
auto l2 = [](int*, double) {};
|
||||
auto l2_const = [](const int*, double) {};
|
||||
auto l2_noexcept = [](int*, double) noexcept {};
|
||||
auto l2_const_noexcept = [](const int*, double) noexcept {};
|
||||
|
||||
struct NonConstInvocable {
|
||||
void operator()(int*) noexcept {}
|
||||
};
|
||||
|
||||
struct A {
|
||||
int i;
|
||||
void f() {}
|
||||
void f_const() const {}
|
||||
void f_noexcept() noexcept {}
|
||||
void f_const_noexcept() const noexcept {}
|
||||
void g(int&) {}
|
||||
void g_const(int&) const {}
|
||||
void g_noexcept(int&) noexcept {}
|
||||
void g_const_noexcept(int&) const noexcept {}
|
||||
};
|
||||
|
||||
// non-const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, const int*>);
|
||||
|
||||
// LWG issue 4256
|
||||
// https://cplusplus.github.io/LWG/issue4256
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, int*>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int*>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A*>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int*>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int*>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A*>);
|
||||
|
||||
// non-const noexcept
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int*>);
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, int*>);
|
||||
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A*>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
|
||||
static_assert(
|
||||
std::
|
||||
is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int*>);
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
|
||||
|
||||
// const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, const int*>);
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, int*>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2_const>, int*>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A*>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, int*>);
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1_const>, const int*>);
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2_const>, int*>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A*>);
|
||||
|
||||
// const noexcept
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const_noexcept>, int*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int*>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_const>, int*>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int*>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
|
||||
std::constant_wrapper<NonConstInvocable{}>,
|
||||
int*>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(double) const noexcept>,
|
||||
std::constant_wrapper<l2_const_noexcept>,
|
||||
int*>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_const_noexcept>, int*>);
|
||||
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A*>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A*>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
|
||||
std::constant_wrapper<&A::g_const_noexcept>,
|
||||
A*>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
|
||||
std::constant_wrapper<l1_const_noexcept>,
|
||||
int*>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const noexcept>,
|
||||
std::constant_wrapper<l2_const_noexcept>,
|
||||
int*>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const noexcept>,
|
||||
std::constant_wrapper<&A::f_const_noexcept>,
|
||||
A*>);
|
||||
|
||||
double f1(const int* x, double y) noexcept { return *x + y; }
|
||||
|
||||
struct M {
|
||||
int i;
|
||||
int f() { return i; }
|
||||
int f_const() const { return i + 5; }
|
||||
int f_noexcept() noexcept { return i + 7; }
|
||||
int f_const_noexcept() const noexcept { return i + 9; }
|
||||
int g(int& j) {
|
||||
j = 42;
|
||||
return i + j;
|
||||
}
|
||||
int g_const(int& j) const {
|
||||
j = 42;
|
||||
return i + j + 1;
|
||||
}
|
||||
int g_noexcept(int& j) noexcept {
|
||||
j = 42;
|
||||
return i + j + 2;
|
||||
}
|
||||
int g_const_noexcept(int& j) const noexcept {
|
||||
j = 42;
|
||||
return i + j + 3;
|
||||
}
|
||||
};
|
||||
|
||||
struct Overload {
|
||||
constexpr int operator()(int*) const noexcept { return 4; }
|
||||
constexpr int operator()(const int*) const noexcept { return 5; }
|
||||
};
|
||||
|
||||
struct Int {
|
||||
int i;
|
||||
constexpr Int(int ii) noexcept : i(ii) {}
|
||||
};
|
||||
|
||||
struct NeedsConversion {
|
||||
int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
|
||||
};
|
||||
|
||||
int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
int i = 0;
|
||||
std::function_ref<void()> f(std::cw<[](int*) {}>, &i);
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
{
|
||||
// explicit
|
||||
int i = 0;
|
||||
std::function_ref<void()> f = {std::cw<[](int*) {}>, &i};
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
{
|
||||
// mutate
|
||||
int i;
|
||||
std::function_ref<double(double)> f(
|
||||
std::cw<[](int* j, double d) {
|
||||
*j = 5;
|
||||
return *j + d;
|
||||
}>,
|
||||
&i);
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(3.3) == 8.3);
|
||||
assert(i == 5);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
int i = 5;
|
||||
std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 47);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
const int i = 5;
|
||||
std::function_ref<int() const> f(std::cw<[](const int* pi) { return *pi + 42; }>, &i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 47);
|
||||
}
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
int i = 5;
|
||||
std::function_ref<double(double) noexcept> f(std::cw<&f1>, &i);
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2.0) == 7.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
int i = 5;
|
||||
std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2.0) == 7.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
const int i = 5;
|
||||
std::function_ref<double(double) const noexcept> f(std::cw<&f1>, &i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2.0) == 7.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// member ptr
|
||||
M m{3};
|
||||
std::function_ref<int()> f(std::cw<&M::f>, &m);
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 3);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
std::function_ref<int(int&)> g(std::cw<&M::g>, &m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(g(j) == 45);
|
||||
assert(j == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// member ptr
|
||||
// const
|
||||
M m{3};
|
||||
std::function_ref<int() const> f_const(std::cw<&M::f_const>, &m);
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_const() == 8);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, &m);
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(g_const(j) == 46);
|
||||
assert(j == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// member ptr
|
||||
// noexcept
|
||||
M m{3};
|
||||
std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, &m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_noexcept() == 10);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, &m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(g_noexcept(j) == 47);
|
||||
assert(j == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// member ptr
|
||||
// const noexcept
|
||||
M m{3};
|
||||
std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, &m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_const_noexcept() == 12);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, &m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(g_const_noexcept(j) == 48);
|
||||
assert(j == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions
|
||||
int i = 1;
|
||||
std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4(2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions function pointer
|
||||
int i = 1;
|
||||
std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4(2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// CV forwarded correctly
|
||||
int i = 0;
|
||||
std::function_ref<int()> f(std::cw<Overload{}>, &i);
|
||||
std::function_ref<int() const> f_const(std::cw<Overload{}>, &i);
|
||||
std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, &i);
|
||||
std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, &i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 4);
|
||||
assert(f_const() == 5);
|
||||
assert(f_noexcept() == 4);
|
||||
assert(f_const_noexcept() == 5);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,428 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// template<auto f, class U>
|
||||
// constexpr function_ref(constant_wrapper<f>, U&& obj) noexcept;
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// Constraints:
|
||||
// - is_rvalue_reference_v<U&&> is false, and
|
||||
// - is-invocable-using<const F&, cv T&> is true.
|
||||
|
||||
auto l1 = [](int) {};
|
||||
auto l1_noexcept = [](int) noexcept {};
|
||||
auto l2 = [](int, double) {};
|
||||
auto l2_noexcept = [](int, double) noexcept {};
|
||||
auto l3 = [](int&) {};
|
||||
auto l3_noexcept = [](int&) noexcept {};
|
||||
|
||||
struct NonConstInvocable {
|
||||
void operator()(long) noexcept {}
|
||||
};
|
||||
|
||||
struct A {
|
||||
int i;
|
||||
void f() {}
|
||||
void f_const() const {}
|
||||
void f_noexcept() noexcept {}
|
||||
void f_const_noexcept() const noexcept {}
|
||||
void g(int&) {}
|
||||
void g_const(int&) const {}
|
||||
void g_noexcept(int&) noexcept {}
|
||||
void g_const_noexcept(int&) const noexcept {}
|
||||
};
|
||||
|
||||
// non-const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&&>);
|
||||
|
||||
// LWG issue 4256
|
||||
// https://cplusplus.github.io/LWG/issue4256
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<NonConstInvocable{}>, long&>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l2>, int&>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, const int&>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::g>, A&>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l1>, int&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double)>, std::constant_wrapper<l2>, int&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<l3>, int&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::constant_wrapper<&A::f>, A&>);
|
||||
|
||||
// non-const noexcept
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1>, int&>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<NonConstInvocable{}>, long&>);
|
||||
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2>, int&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3>, const int&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f>, A&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::g_noexcept>, A&>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
|
||||
static_assert(
|
||||
std::
|
||||
is_nothrow_constructible_v<std::function_ref<void(double) noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void() noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
|
||||
|
||||
// const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&&>);
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<NonConstInvocable{}>, long&>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l2>, int&>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, int&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l3>, const int&>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f>, A&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::g_const>, A&>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<l1>, int&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(double) const>, std::constant_wrapper<l2>, int&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void() const>, std::constant_wrapper<&A::f_const>, A&>);
|
||||
|
||||
// const noexcept
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1>, int&>);
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&&>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
|
||||
std::constant_wrapper<NonConstInvocable{}>,
|
||||
long&>);
|
||||
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2>, int&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
|
||||
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, int&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3>, const int&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l3_noexcept>, const int&>);
|
||||
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f>, A&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const>, A&>);
|
||||
static_assert(
|
||||
!std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_noexcept>, A&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() const noexcept>,
|
||||
std::constant_wrapper<&A::g_const_noexcept>,
|
||||
A&>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<l1_noexcept>, int&>);
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void(double) const noexcept>, std::constant_wrapper<l2_noexcept>, int&>);
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void() const noexcept>, std::constant_wrapper<&A::f_const_noexcept>, A&>);
|
||||
|
||||
double f1(int x, double y) noexcept { return x + y; }
|
||||
|
||||
struct M {
|
||||
int i;
|
||||
int f() { return i; }
|
||||
int f_const() const { return i + 5; }
|
||||
int f_noexcept() noexcept { return i + 7; }
|
||||
int f_const_noexcept() const noexcept { return i + 9; }
|
||||
int g(int& j) {
|
||||
j = 42;
|
||||
return i + j;
|
||||
}
|
||||
int g_const(int& j) const {
|
||||
j = 42;
|
||||
return i + j + 1;
|
||||
}
|
||||
int g_noexcept(int& j) noexcept {
|
||||
j = 42;
|
||||
return i + j + 2;
|
||||
}
|
||||
int g_const_noexcept(int& j) const noexcept {
|
||||
j = 42;
|
||||
return i + j + 3;
|
||||
}
|
||||
};
|
||||
|
||||
struct Overload {
|
||||
constexpr int operator()(int&) const noexcept { return 4; }
|
||||
constexpr int operator()(const int&) const noexcept { return 5; }
|
||||
};
|
||||
|
||||
struct Int {
|
||||
int i;
|
||||
constexpr Int(int ii) noexcept : i(ii) {}
|
||||
};
|
||||
|
||||
struct NeedsConversion {
|
||||
int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
|
||||
};
|
||||
|
||||
int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
int i = 0;
|
||||
std::function_ref<void()> f(std::cw<[](int) {}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
{
|
||||
// explicit
|
||||
int i = 0;
|
||||
std::function_ref<void()> f = {std::cw<[](int) {}>, i};
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
{
|
||||
// mutate
|
||||
int i;
|
||||
std::function_ref<double(double)> f(
|
||||
std::cw<[](int& j, double d) {
|
||||
j = 5;
|
||||
return j + d;
|
||||
}>,
|
||||
i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(3.3) == 8.3);
|
||||
assert(i == 5);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
int i = 5;
|
||||
std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 47);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
const int i = 5;
|
||||
std::function_ref<int() const> f(std::cw<[](int j) { return j + 42; }>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 47);
|
||||
}
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
int i = 5;
|
||||
std::function_ref<double(double) noexcept> f(std::cw<&f1>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2.0) == 7.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
int i = 5;
|
||||
std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2.0) == 7.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
const int i = 5;
|
||||
std::function_ref<double(double) const noexcept> f(std::cw<&f1>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2.0) == 7.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// member ptr
|
||||
M m{3};
|
||||
std::function_ref<int()> f(std::cw<&M::f>, m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 3);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
std::function_ref<int(int&)> g(std::cw<&M::g>, m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(g(j) == 45);
|
||||
assert(j == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// member ptr
|
||||
// const
|
||||
M m{3};
|
||||
std::function_ref<int() const> f_const(std::cw<&M::f_const>, m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_const() == 8);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
std::function_ref<int(int&)> g_const(std::cw<&M::g_const>, m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(g_const(j) == 46);
|
||||
assert(j == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// member ptr
|
||||
// noexcept
|
||||
M m{3};
|
||||
std::function_ref<int() noexcept> f_noexcept(std::cw<&M::f_noexcept>, m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_noexcept() == 10);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
std::function_ref<int(int&) noexcept> g_noexcept(std::cw<&M::g_noexcept>, m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(g_noexcept(j) == 47);
|
||||
assert(j == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// member ptr
|
||||
// const noexcept
|
||||
M m{3};
|
||||
std::function_ref<int() const noexcept> f_const_noexcept(std::cw<&M::f_const_noexcept>, m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_const_noexcept() == 12);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
std::function_ref<int(int&) const noexcept> g_const_noexcept(std::cw<&M::g_const_noexcept>, m);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(g_const_noexcept(j) == 48);
|
||||
assert(j == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions
|
||||
int i = 1;
|
||||
|
||||
std::function_ref<Int(int, int)> f(std::cw<NeedsConversion{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) const> f2(std::cw<NeedsConversion{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) noexcept> f3(std::cw<NeedsConversion{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) const noexcept> f4(std::cw<NeedsConversion{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4(2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions function pointer
|
||||
int i = 1;
|
||||
std::function_ref<Int(int, int)> f(std::cw<&needs_conversion>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) const> f2(std::cw<&needs_conversion>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) noexcept> f3(std::cw<&needs_conversion>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3(2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int) const noexcept> f4(std::cw<&needs_conversion>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4(2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// volatile objects
|
||||
volatile int i = 5;
|
||||
std::function_ref<int() > f(std::cw<[](int j) { return j + 42; }>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 47);
|
||||
}
|
||||
}
|
||||
{
|
||||
// CV forwarded correctly
|
||||
int i = 0;
|
||||
std::function_ref<int()> f(std::cw<Overload{}>, i);
|
||||
std::function_ref<int() const> f_const(std::cw<Overload{}>, i);
|
||||
std::function_ref<int() noexcept> f_noexcept(std::cw<Overload{}>, i);
|
||||
std::function_ref<int() const noexcept> f_const_noexcept(std::cw<Overload{}>, i);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 4);
|
||||
assert(f_const() == 5);
|
||||
assert(f_noexcept() == 4);
|
||||
assert(f_const_noexcept() == 5);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// constexpr function_ref(const function_ref&) noexcept = default;
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
static_assert(std::is_copy_constructible_v<std::function_ref<void()>>);
|
||||
static_assert(std::is_copy_constructible_v<std::function_ref<void() const>>);
|
||||
static_assert(std::is_copy_constructible_v<std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_copy_constructible_v<std::function_ref<void() const noexcept>>);
|
||||
|
||||
static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void()>>);
|
||||
static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const>>);
|
||||
static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_trivially_copy_constructible_v<std::function_ref<void() const noexcept>>);
|
||||
|
||||
double f1(int x, double y) noexcept { return x + y; }
|
||||
|
||||
struct Int {
|
||||
int i;
|
||||
constexpr Int(int ii) noexcept : i(ii) {}
|
||||
};
|
||||
|
||||
struct NeedsConversion {
|
||||
int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
|
||||
};
|
||||
|
||||
int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
std::function_ref<void()> f(std::cw<[] {}>);
|
||||
auto f2 = f;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f2();
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
std::function_ref<int() const> f(std::cw<[] { return 42; }>);
|
||||
auto f2 = f;
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
|
||||
auto f2 = f;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
|
||||
auto f2 = f;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions
|
||||
std::function_ref<Int(int, int, int)> f(std::cw<NeedsConversion{}>);
|
||||
auto f_copy = f;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_copy(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const> f2(std::cw<NeedsConversion{}>);
|
||||
auto f2_copy = f2;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2_copy(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) noexcept> f3(std::cw<NeedsConversion{}>);
|
||||
auto f3_copy = f3;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3_copy(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<NeedsConversion{}>);
|
||||
auto f4_copy = f4;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4_copy(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions function pointer
|
||||
std::function_ref<Int(int, int, int)> f(std::cw<&needs_conversion>);
|
||||
auto f_copy = f;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_copy(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const> f2(std::cw<&needs_conversion>);
|
||||
auto f2_copy = f2;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2_copy(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) noexcept> f3(std::cw<&needs_conversion>);
|
||||
auto f3_copy = f3;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3_copy(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const noexcept> f4(std::cw<&needs_conversion>);
|
||||
auto f4_copy = f4;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4_copy(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// constexpr function_ref& operator=(const function_ref&) noexcept = default;
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
static_assert(std::is_copy_assignable_v<std::function_ref<void()>>);
|
||||
static_assert(std::is_copy_assignable_v<std::function_ref<void() const>>);
|
||||
static_assert(std::is_copy_assignable_v<std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_copy_assignable_v<std::function_ref<void() const noexcept>>);
|
||||
|
||||
static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void()>>);
|
||||
static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const>>);
|
||||
static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_trivially_copy_assignable_v<std::function_ref<void() const noexcept>>);
|
||||
|
||||
double plus(int x, double y) noexcept { return x + y; }
|
||||
double minus(int x, double y) noexcept { return x - y; }
|
||||
|
||||
struct Int {
|
||||
int i;
|
||||
constexpr Int(int ii) noexcept : i(ii) {}
|
||||
};
|
||||
|
||||
struct NeedsConversion {
|
||||
int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
|
||||
};
|
||||
|
||||
int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
|
||||
int zero(Int, Int, Int) noexcept { return 0; }
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
std::function_ref<void()> f(std::cw<[] {}>);
|
||||
std::function_ref<void()> f2(std::cw<[] {}>);
|
||||
std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = f;
|
||||
assert(&result == &f2);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
f2();
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
std::function_ref<int() const> f(std::cw<[] { return 42; }>);
|
||||
std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
|
||||
std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = f;
|
||||
assert(&result == &f2);
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 42);
|
||||
assert(f2() == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
|
||||
std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
|
||||
std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = f;
|
||||
assert(&result == &f2);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2.0) == 3.0);
|
||||
assert(f2(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
|
||||
std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
|
||||
std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = f;
|
||||
assert(&result == &f2);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2.0) == 3.0);
|
||||
assert(f2(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions
|
||||
std::function_ref<Int(int, int, int)> f(std::cw<[](int, int, int) { return Int{1}; }>);
|
||||
std::function_ref<Int(int, int, int)> f2(std::cw<NeedsConversion{}>);
|
||||
std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
|
||||
assert(&result == &f);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2, 3).i == 6);
|
||||
assert(f2(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// with conversions
|
||||
// const
|
||||
std::function_ref<Int(int, int, int) const> f_const(std::cw<[](int, int, int) { return Int{1}; }>);
|
||||
std::function_ref<Int(int, int, int) const> f2_const(std::cw<NeedsConversion{}>);
|
||||
std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
|
||||
assert(&result == &f_const);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_const(1, 2, 3).i == 6);
|
||||
assert(f2_const(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions
|
||||
// noexcept
|
||||
std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<[](int, int, int) noexcept { return Int{1}; }>);
|
||||
std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<NeedsConversion{}>);
|
||||
std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
|
||||
assert(&result == &f_noexcept);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_noexcept(1, 2, 3).i == 6);
|
||||
assert(f2_noexcept(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions
|
||||
// const noexcept
|
||||
std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(
|
||||
std::cw<[](int, int, int) noexcept { return Int{1}; }>);
|
||||
std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<NeedsConversion{}>);
|
||||
std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
|
||||
f2_const_noexcept;
|
||||
assert(&result == &f_const_noexcept);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_const_noexcept(1, 2, 3).i == 6);
|
||||
assert(f2_const_noexcept(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions function pointer
|
||||
std::function_ref<Int(int, int, int)> f(std::cw<&zero>);
|
||||
std::function_ref<Int(int, int, int)> f2(std::cw<&needs_conversion>);
|
||||
std::same_as<std::function_ref<Int(int, int, int)>&> decltype(auto) result = f = f2;
|
||||
assert(&result == &f);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2, 3).i == 6);
|
||||
assert(f2(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// with conversions function pointer
|
||||
// const
|
||||
std::function_ref<Int(int, int, int) const> f_const(std::cw<&zero>);
|
||||
std::function_ref<Int(int, int, int) const> f2_const(std::cw<&needs_conversion>);
|
||||
std::same_as<std::function_ref<Int(int, int, int) const>&> decltype(auto) result = f_const = f2_const;
|
||||
assert(&result == &f_const);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_const(1, 2, 3).i == 6);
|
||||
assert(f2_const(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions function pointer
|
||||
// noexcept
|
||||
std::function_ref<Int(int, int, int) noexcept> f_noexcept(std::cw<&zero>);
|
||||
std::function_ref<Int(int, int, int) noexcept> f2_noexcept(std::cw<&needs_conversion>);
|
||||
std::same_as<std::function_ref<Int(int, int, int) noexcept>&> decltype(auto) result = f_noexcept = f2_noexcept;
|
||||
assert(&result == &f_noexcept);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_noexcept(1, 2, 3).i == 6);
|
||||
assert(f2_noexcept(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// with conversions function pointer
|
||||
// const noexcept
|
||||
std::function_ref<Int(int, int, int) const noexcept> f_const_noexcept(std::cw<&zero>);
|
||||
std::function_ref<Int(int, int, int) const noexcept> f2_const_noexcept(std::cw<&needs_conversion>);
|
||||
std::same_as<std::function_ref<Int(int, int, int) const noexcept>&> decltype(auto) result = f_const_noexcept =
|
||||
f2_const_noexcept;
|
||||
assert(&result == &f_const_noexcept);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f_const_noexcept(1, 2, 3).i == 6);
|
||||
assert(f2_const_noexcept(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// template<class F> function_ref(F* f) noexcept;
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// Constraints:
|
||||
// - is_function_v<F> is true, and
|
||||
// - is-invocable-using<F> is true.
|
||||
|
||||
struct A {
|
||||
int i;
|
||||
void f() {}
|
||||
void operator()(auto...) const {}
|
||||
};
|
||||
|
||||
// non-const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)()>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, void*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, void (*)(int)>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)()>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, void (*)() noexcept>);
|
||||
|
||||
// non-const noexcept(true)
|
||||
static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A (*)()>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, A*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (*)(int) noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, A (*)() noexcept>);
|
||||
|
||||
// const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, A)>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double)>);
|
||||
static_assert(
|
||||
std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, void (*)(int, double) noexcept>);
|
||||
|
||||
// const noexcept(true)
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double) noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, double)>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (*)(int, A) noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
|
||||
void (*)(int, double) noexcept>);
|
||||
|
||||
int fn() { return 42; }
|
||||
|
||||
int fn_maythrow(int i, A a) { return i - a.i; }
|
||||
int fn_noexcept(int i, A a) noexcept { return i + a.i; }
|
||||
|
||||
void foo(int) {}
|
||||
void bar(int) noexcept {}
|
||||
struct Int {
|
||||
int i;
|
||||
Int(int ii) noexcept : i(ii) {}
|
||||
};
|
||||
|
||||
int needs_conversion(Int x, Int y, Int z) noexcept { return x.i + y.i + z.i; }
|
||||
|
||||
void test() {
|
||||
{
|
||||
// simple case
|
||||
std::function_ref<int()> f(&fn);
|
||||
assert(f() == 42);
|
||||
}
|
||||
{
|
||||
// explicit(false)
|
||||
std::function_ref<int()> f = &fn;
|
||||
assert(f() == 42);
|
||||
}
|
||||
{
|
||||
std::function_ref<int(int, A)> f(&fn_noexcept);
|
||||
assert(f(4, A{5}) == 9);
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
std::function_ref<int(int, A) noexcept> f(&fn_noexcept);
|
||||
assert(f(4, A{5}) == 9);
|
||||
}
|
||||
{
|
||||
// const
|
||||
auto l = [](int x, int y, int z) { return x + y - z; };
|
||||
std::function_ref<int(int, int, int) const> f(+l);
|
||||
assert(f(2, 3, 4) == 1);
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
std::function_ref<int(int, A) const noexcept> f(&fn_noexcept);
|
||||
assert(f(4, A{5}) == 9);
|
||||
}
|
||||
|
||||
{
|
||||
std::function_ref<Int(int, int, int)> f(&needs_conversion);
|
||||
assert(f(1, 2, 3).i == 6);
|
||||
|
||||
std::function_ref<Int(int, int, int) const> f2(&needs_conversion);
|
||||
assert(f2(1, 2, 3).i == 6);
|
||||
|
||||
std::function_ref<Int(int, int, int) noexcept> f3(&needs_conversion);
|
||||
assert(f3(1, 2, 3).i == 6);
|
||||
|
||||
std::function_ref<Int(int, int, int) const noexcept> f4(&needs_conversion);
|
||||
assert(f4(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
static_assert(std::is_move_constructible_v<std::function_ref<void()>>);
|
||||
static_assert(std::is_move_constructible_v<std::function_ref<void() const>>);
|
||||
static_assert(std::is_move_constructible_v<std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_move_constructible_v<std::function_ref<void() const noexcept>>);
|
||||
|
||||
static_assert(std::is_trivially_move_constructible_v<std::function_ref<void()>>);
|
||||
static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const>>);
|
||||
static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_trivially_move_constructible_v<std::function_ref<void() const noexcept>>);
|
||||
|
||||
double f1(int x, double y) noexcept { return x + y; }
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
std::function_ref<void()> f(std::cw<[] {}>);
|
||||
auto f2 = std::move(f);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f2();
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
std::function_ref<int() const> f(std::cw<[] { return 42; }>);
|
||||
auto f2 = std::move(f);
|
||||
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
std::function_ref<double(int, double) noexcept> f(std::cw<&f1>);
|
||||
auto f2 = std::move(f);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
std::function_ref<double(int, double) const noexcept> f(std::cw<&f1>);
|
||||
auto f2 = std::move(f);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,86 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
static_assert(std::is_move_assignable_v<std::function_ref<void()>>);
|
||||
static_assert(std::is_move_assignable_v<std::function_ref<void() const>>);
|
||||
static_assert(std::is_move_assignable_v<std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_move_assignable_v<std::function_ref<void() const noexcept>>);
|
||||
|
||||
static_assert(std::is_trivially_move_assignable_v<std::function_ref<void()>>);
|
||||
static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const>>);
|
||||
static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() noexcept>>);
|
||||
static_assert(std::is_trivially_move_assignable_v<std::function_ref<void() const noexcept>>);
|
||||
|
||||
double plus(int x, double y) noexcept { return x + y; }
|
||||
double minus(int x, double y) noexcept { return x - y; }
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
std::function_ref<void()> f(std::cw<[] {}>);
|
||||
std::function_ref<void()> f2(std::cw<[] {}>);
|
||||
std::same_as<std::function_ref<void()>&> decltype(auto) result = f2 = std::move(f);
|
||||
assert(&result == &f2);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
f2();
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
std::function_ref<int() const> f(std::cw<[] { return 42; }>);
|
||||
std::function_ref<int() const> f2(std::cw<[] { return 41; }>);
|
||||
std::same_as<std::function_ref<int() const>&> decltype(auto) result = f2 = std::move(f);
|
||||
assert(&result == &f2);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 42);
|
||||
assert(f2() == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
std::function_ref<double(int, double) noexcept> f(std::cw<&plus>);
|
||||
std::function_ref<double(int, double) noexcept> f2(std::cw<&minus>);
|
||||
std::same_as<std::function_ref<double(int, double) noexcept>&> decltype(auto) result = f2 = std::move(f);
|
||||
assert(&result == &f2);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2.0) == 3.0);
|
||||
assert(f2(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
std::function_ref<double(int, double) const noexcept> f(std::cw<&plus>);
|
||||
std::function_ref<double(int, double) const noexcept> f2(std::cw<&minus>);
|
||||
std::same_as<std::function_ref<double(int, double) const noexcept>&> decltype(auto) result = f2 = std::move(f);
|
||||
assert(&result == &f2);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2.0) == 3.0);
|
||||
assert(f2(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,368 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// template<class F> constexpr function_ref(F&&) noexcept;
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// Constraints:
|
||||
// - remove_cvref_t<F> is not the same type as function_ref,
|
||||
// - is_member_pointer_v<T> is false, and
|
||||
// - is-invocable-using<cv T&> is true.
|
||||
|
||||
struct A {
|
||||
int i;
|
||||
void f() {}
|
||||
void operator()(auto...) const {}
|
||||
};
|
||||
|
||||
constexpr auto l1 = [] {};
|
||||
using L1 = std::remove_cvref_t<decltype(l1)>;
|
||||
|
||||
constexpr auto l2 = [] { return A{5}; };
|
||||
using L2 = std::remove_cvref_t<decltype(l2)>;
|
||||
|
||||
constexpr auto l2_noexcept = [] noexcept { return A{5}; };
|
||||
using L2Noexcept = std::remove_cvref_t<decltype(l2_noexcept)>;
|
||||
|
||||
constexpr auto l3 = [](int x, double d) { return x + d; };
|
||||
using L3 = std::remove_cvref_t<decltype(l3)>;
|
||||
|
||||
constexpr auto l3_noexcept = [](int x, double d) noexcept { return x + d; };
|
||||
using L3Noexcept = std::remove_cvref_t<decltype(l3_noexcept)>;
|
||||
|
||||
struct NonConstInvocable {
|
||||
int i;
|
||||
|
||||
int operator()(int x, double y) {
|
||||
++i;
|
||||
return x + y + i;
|
||||
}
|
||||
};
|
||||
|
||||
// non-const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, L1&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, L1&&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, L1 const&&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, L2&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, void (A::*)()>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(A*)>, void (A::*)()>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1&&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, L1 const&&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void()>, std::function_ref<int()>&>);
|
||||
|
||||
// non-const noexcept(true)
|
||||
static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<A() noexcept>, L2 const&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void()>, L3&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void() noexcept>, void (A::*)() noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(A*) noexcept>, void (A::*)() noexcept>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept&&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, L2Noexcept const&&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<A() noexcept>, std::function_ref<A&() noexcept>>);
|
||||
|
||||
// const noexcept(false)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
|
||||
static_assert(
|
||||
std::is_constructible_v<std::function_ref<void(int, double) const>, std::function_ref<int(int, double) const>>);
|
||||
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double)>, NonConstInvocable&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double)>, const NonConstInvocable&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, NonConstInvocable&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const>, void (A::*)()>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)()>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, L3&&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>, const L3&&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const>,
|
||||
std::function_ref<int(int, double) const>>);
|
||||
|
||||
// const noexcept(true)
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
|
||||
static_assert(std::is_constructible_v<std::function_ref<void(int, double) const noexcept>,
|
||||
std::function_ref<int(int, double) const noexcept>>);
|
||||
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, L3&&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3&&>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void*>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(int, double) const noexcept>, void (A::*)() noexcept>);
|
||||
static_assert(!std::is_constructible_v<std::function_ref<void(A*) const>, void (A::*)() noexcept>);
|
||||
|
||||
// the constructor is noexcept
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, L3Noexcept&&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>, const L3Noexcept&&>);
|
||||
static_assert(std::is_nothrow_constructible_v<std::function_ref<void(int, double) const noexcept>,
|
||||
std::function_ref<int(int, double) const noexcept>>);
|
||||
|
||||
struct F {
|
||||
int i;
|
||||
int operator()(auto&&...) { return 5 + i; }
|
||||
|
||||
int operator()(auto&&...) const { return 6 + i; }
|
||||
};
|
||||
|
||||
struct Int {
|
||||
int i;
|
||||
constexpr Int(int ii) noexcept : i(ii) {}
|
||||
};
|
||||
|
||||
struct NeedsConversion {
|
||||
int operator()(Int x, Int y, Int z) const noexcept { return x.i + y.i + z.i; }
|
||||
};
|
||||
|
||||
int fn() { return 5; }
|
||||
constexpr auto fn_noexcept = []() noexcept { return 6; };
|
||||
|
||||
const auto one = []() noexcept { return 1; };
|
||||
const auto two = []() noexcept { return 2; };
|
||||
|
||||
struct VolatileCallable {
|
||||
int operator()() volatile const { return 42; }
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
std::function_ref<void()> f(l1);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
{
|
||||
// explicit(false)
|
||||
std::function_ref<void()> f = l1;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
{
|
||||
// noexcept
|
||||
std::function_ref<A() noexcept> f(l2_noexcept);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
auto a = f();
|
||||
assert(a.i == 5);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const
|
||||
std::function_ref<double(int, double) const> f(l3);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const noexcept
|
||||
std::function_ref<double(int, double) const noexcept> f(l3_noexcept);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2.0) == 3.0);
|
||||
}
|
||||
}
|
||||
{
|
||||
// no copies of original callable
|
||||
auto local = [i = 5] mutable { return i++; };
|
||||
std::function_ref<int()> f(local);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 5);
|
||||
assert(local() == 6);
|
||||
assert(f() == 7);
|
||||
}
|
||||
}
|
||||
{
|
||||
// const correctness
|
||||
F f{5};
|
||||
|
||||
std::function_ref<int()> f1(f);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f1() == 10);
|
||||
assert(std::as_const(f1)() == 10);
|
||||
}
|
||||
|
||||
std::function_ref<int() const> f2(f);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 11);
|
||||
assert(std::as_const(f2)() == 11);
|
||||
}
|
||||
}
|
||||
{
|
||||
// with conversions
|
||||
NeedsConversion c{};
|
||||
|
||||
std::function_ref<Int(int, int, int)> f(c);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const> f2(c);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) noexcept> f3(c);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f3(1, 2, 3).i == 6);
|
||||
}
|
||||
|
||||
std::function_ref<Int(int, int, int) const noexcept> f4(c);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f4(1, 2, 3).i == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// noexcept conversion
|
||||
std::function_ref<int() noexcept> f1(fn_noexcept);
|
||||
std::function_ref<int()> f2(f1);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// const conversion
|
||||
std::function_ref<int() const> f1(fn_noexcept);
|
||||
std::function_ref<int()> f2(f1);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// const noexcept conversion
|
||||
std::function_ref<int() const noexcept> f1(fn_noexcept);
|
||||
std::function_ref<int()> f2(f1);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// const noexcept to noexcept conversion
|
||||
std::function_ref<int() const noexcept> f1(fn_noexcept);
|
||||
std::function_ref<int() noexcept> f2(f1);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// const noexcept to const conversion
|
||||
std::function_ref<int() const noexcept> f1(fn_noexcept);
|
||||
std::function_ref<int() const> f2(f1);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 6);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// P3961R1 Less double indirection in function_ref
|
||||
// double unwrapping
|
||||
std::function_ref<int() const noexcept> f1(std::cw<one>);
|
||||
std::function_ref<int()> f2(f1);
|
||||
|
||||
f1 = std::cw<two>;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 1 || f2() == 2);
|
||||
LIBCPP_ASSERT(f2() == 1);
|
||||
}
|
||||
}
|
||||
{
|
||||
// P3961R1 Less double indirection in function_ref
|
||||
// is-convertible-from-specialization<remove_cv_t<T>> is false
|
||||
std::function_ref<int()> f1(std::cw<one>);
|
||||
std::function_ref<int() const> f2(f1);
|
||||
|
||||
f1 = std::cw<two>;
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2() == 2);
|
||||
}
|
||||
}
|
||||
{
|
||||
// volatile objects
|
||||
const volatile VolatileCallable vc{};
|
||||
std::function_ref<int()> f(vc);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f() == 42);
|
||||
}
|
||||
}
|
||||
{
|
||||
// static call operator
|
||||
struct StaticCall {
|
||||
static int operator()(int x, int y) { return x + y; }
|
||||
};
|
||||
StaticCall sc;
|
||||
std::function_ref<int(int, int)> f(sc);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f(1, 2) == 3);
|
||||
}
|
||||
}
|
||||
{
|
||||
// overload set with static and non-static call operator
|
||||
struct OverloadSet {
|
||||
static int operator()(int x) { return x + 1; }
|
||||
int operator()(long x) const { return x + 2; }
|
||||
};
|
||||
OverloadSet os;
|
||||
std::function_ref<int(int)> f1(os);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f1(1) == 2);
|
||||
}
|
||||
|
||||
LIBCPP_STATIC_ASSERT(!std::__statically_callable<OverloadSet, long>);
|
||||
std::function_ref<int(long)> f2(os);
|
||||
if (!TEST_IS_CONSTANT_EVALUATED) {
|
||||
assert(f2(1L) == 3);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,524 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// R operator()(ArgTypes... args) const noexcept(noex);
|
||||
|
||||
#include <cassert>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "test_macros.h"
|
||||
#include "MoveOnly.h"
|
||||
|
||||
template <class T, class... Args>
|
||||
concept ConstInvocable = requires(const T t, Args... args) {
|
||||
{ t(std::forward<Args>(args)...) };
|
||||
};
|
||||
|
||||
template <class T, class... Args>
|
||||
concept ConstNoexceptInvocable = requires(const T t, Args... args) {
|
||||
{ t(std::forward<Args>(args)...) } noexcept;
|
||||
};
|
||||
|
||||
static_assert(ConstInvocable<std::function_ref<void()>>);
|
||||
static_assert(!ConstNoexceptInvocable<std::function_ref<void()>>);
|
||||
|
||||
static_assert(ConstInvocable<std::function_ref<void() noexcept>>);
|
||||
static_assert(ConstNoexceptInvocable<std::function_ref<void() noexcept>>);
|
||||
|
||||
static_assert(ConstInvocable<std::function_ref<void() const>>);
|
||||
static_assert(!ConstNoexceptInvocable<std::function_ref<void() const>>);
|
||||
|
||||
static_assert(ConstInvocable<std::function_ref<void() const noexcept>>);
|
||||
static_assert(ConstNoexceptInvocable<std::function_ref<void() const noexcept>>);
|
||||
|
||||
struct S {
|
||||
int data = 42;
|
||||
int operator()(int& x) const noexcept { return data + x; }
|
||||
int operator()(const int& x) const noexcept { return data + x + 1; }
|
||||
int operator()(int&& x) const noexcept { return data + x + 2; }
|
||||
int operator()(const int&& x) const noexcept { return data + x + 3; }
|
||||
};
|
||||
|
||||
struct S2 {
|
||||
double operator()(int x, int y, int z) noexcept { return x + y + z; }
|
||||
|
||||
double operator()(int x, int y, int z) const noexcept { return x + y + z + 1; }
|
||||
};
|
||||
|
||||
struct Big {
|
||||
char c[128];
|
||||
};
|
||||
|
||||
struct S3Ref {
|
||||
char operator()(Big& b) const noexcept { return b.c[0]; }
|
||||
};
|
||||
|
||||
struct S3Value {
|
||||
char operator()(Big b) const noexcept { return b.c[0]; }
|
||||
};
|
||||
|
||||
struct TrackCopyMove {
|
||||
mutable int copy_count = 0;
|
||||
int move_count = 0;
|
||||
|
||||
TrackCopyMove() = default;
|
||||
TrackCopyMove(const TrackCopyMove& other) : copy_count(other.copy_count), move_count(other.move_count) {
|
||||
++copy_count;
|
||||
++other.copy_count;
|
||||
}
|
||||
|
||||
TrackCopyMove(TrackCopyMove&& other) noexcept : copy_count(other.copy_count), move_count(other.move_count) {
|
||||
++move_count;
|
||||
++other.move_count;
|
||||
}
|
||||
TrackCopyMove& operator=(const TrackCopyMove& other) {
|
||||
++copy_count;
|
||||
++other.copy_count;
|
||||
return *this;
|
||||
}
|
||||
TrackCopyMove& operator=(TrackCopyMove&& other) noexcept {
|
||||
++move_count;
|
||||
++other.move_count;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
void test_default() {
|
||||
{
|
||||
std::function_ref<void()> f = [] {};
|
||||
f();
|
||||
static_assert(std::is_void_v<decltype(f())>);
|
||||
}
|
||||
{
|
||||
// reference
|
||||
int x = 42;
|
||||
std::function_ref<int&(int&)> f(std::cw<[](int& i) -> int& { return i; }>);
|
||||
std::same_as<int&> decltype(auto) r = f(x);
|
||||
assert(&r == &x);
|
||||
}
|
||||
{
|
||||
// Move only
|
||||
std::function_ref<MoveOnly(MoveOnly)> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
|
||||
std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
|
||||
assert(r.get() == 6);
|
||||
}
|
||||
{
|
||||
// different return type
|
||||
std::function_ref<int(int)> f(std::cw<[](int x) -> double { return x + 0.1; }>);
|
||||
std::same_as<int> decltype(auto) r = f(5);
|
||||
assert(r == 5);
|
||||
}
|
||||
{
|
||||
// Args overload resolution
|
||||
S s;
|
||||
int x = 1;
|
||||
std::function_ref<int(int&)> f(s);
|
||||
assert(f(x) == 43);
|
||||
|
||||
std::function_ref<int(const int&)> g(s);
|
||||
assert(g(x) == 44);
|
||||
|
||||
std::function_ref<int(int&&)> h(s);
|
||||
assert(h(std::move(x)) == 45);
|
||||
|
||||
std::function_ref<int(const int&&)> i(s);
|
||||
assert(i(std::move(x)) == 46);
|
||||
}
|
||||
{
|
||||
// const overload
|
||||
S2 s;
|
||||
std::function_ref<double(int, int, int)> f(s);
|
||||
std::same_as<double> decltype(auto) r = f(1, 2, 3);
|
||||
assert(r == 6.0);
|
||||
|
||||
std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
|
||||
assert(r2 == 6.0);
|
||||
|
||||
const S2 s2;
|
||||
std::function_ref<double(int, int, int)> f2(s2);
|
||||
std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
|
||||
assert(r3 == 7.0);
|
||||
|
||||
std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
|
||||
assert(r4 == 7.0);
|
||||
}
|
||||
{
|
||||
// Big object passed
|
||||
Big b{};
|
||||
b.c[0] = 'a';
|
||||
std::function_ref<char(Big&)> f1(std::cw<S3Ref{}>);
|
||||
assert(f1(b) == 'a');
|
||||
std::function_ref<char(Big)> f2(std::cw<S3Value{}>);
|
||||
assert(f2(b) == 'a');
|
||||
}
|
||||
{
|
||||
// Arg type is an lvalue reference, we should not copy or move the object
|
||||
TrackCopyMove t;
|
||||
auto lambda = [&t](TrackCopyMove& tm) {
|
||||
assert(&tm == &t);
|
||||
assert(tm.copy_count == 0);
|
||||
assert(tm.move_count == 0);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove&)> f = lambda;
|
||||
f(t);
|
||||
}
|
||||
{
|
||||
// Arg type is an rvalue reference, we should not copy or move the object
|
||||
TrackCopyMove t;
|
||||
auto lambda = [&t](TrackCopyMove&& tm) {
|
||||
assert(&tm == &t);
|
||||
assert(tm.copy_count == 0);
|
||||
assert(tm.move_count == 0);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove&&)> f = lambda;
|
||||
f(std::move(t));
|
||||
}
|
||||
{
|
||||
// Arg type is a prvalue, we should move but not copy the object
|
||||
// In libc++, where the type is not trivially copyable, the object should be
|
||||
// moved exactly once when passing into the lambda. The internal functions
|
||||
// of function_ref should forward the argument without copying or moving it
|
||||
auto lambda = [](TrackCopyMove tm) {
|
||||
assert(tm.copy_count == 0);
|
||||
LIBCPP_ASSERT(tm.move_count == 1);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove)> f = lambda;
|
||||
f(TrackCopyMove{});
|
||||
}
|
||||
}
|
||||
|
||||
void test_const() {
|
||||
{
|
||||
std::function_ref<void() const> f = [] {};
|
||||
f();
|
||||
static_assert(std::is_void_v<decltype(f())>);
|
||||
}
|
||||
{
|
||||
// reference
|
||||
int x = 42;
|
||||
std::function_ref<int&(int&) const> f(std::cw<[](int& i) -> int& { return i; }>);
|
||||
std::same_as<int&> decltype(auto) r = f(x);
|
||||
assert(&r == &x);
|
||||
}
|
||||
{
|
||||
// Move only
|
||||
std::function_ref<MoveOnly(MoveOnly) const> f(std::cw<[](MoveOnly mo) { return MoveOnly{mo.get() + 5}; }>);
|
||||
std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
|
||||
assert(r.get() == 6);
|
||||
}
|
||||
{
|
||||
// different return type
|
||||
std::function_ref<int(int) const> f(std::cw<[](int x) -> double { return x + 0.1; }>);
|
||||
std::same_as<int> decltype(auto) r = f(5);
|
||||
assert(r == 5);
|
||||
}
|
||||
{
|
||||
// Args overload resolution
|
||||
S s;
|
||||
int x = 1;
|
||||
std::function_ref<int(int&) const> f(s);
|
||||
assert(f(x) == 43);
|
||||
|
||||
std::function_ref<int(const int&) const> g(s);
|
||||
assert(g(x) == 44);
|
||||
|
||||
std::function_ref<int(int&&) const> h(s);
|
||||
assert(h(std::move(x)) == 45);
|
||||
|
||||
std::function_ref<int(const int&&) const> i(s);
|
||||
assert(i(std::move(x)) == 46);
|
||||
}
|
||||
{
|
||||
// const overload
|
||||
S2 s;
|
||||
std::function_ref<double(int, int, int) const> f(s);
|
||||
std::same_as<double> decltype(auto) r = f(1, 2, 3);
|
||||
assert(r == 7.0);
|
||||
|
||||
std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
|
||||
assert(r2 == 7.0);
|
||||
|
||||
const S2 s2;
|
||||
std::function_ref<double(int, int, int) const> f2(s2);
|
||||
std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
|
||||
assert(r3 == 7.0);
|
||||
|
||||
std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
|
||||
assert(r4 == 7.0);
|
||||
}
|
||||
{
|
||||
// Big object passed
|
||||
Big b{};
|
||||
b.c[0] = 'a';
|
||||
std::function_ref<char(Big&) const> f1(std::cw<S3Ref{}>);
|
||||
assert(f1(b) == 'a');
|
||||
std::function_ref<char(Big) const> f2(std::cw<S3Value{}>);
|
||||
assert(f2(b) == 'a');
|
||||
}
|
||||
{
|
||||
// Arg type is an lvalue reference, we should not copy or move the object
|
||||
TrackCopyMove t;
|
||||
auto lambda = [&t](TrackCopyMove& tm) {
|
||||
assert(&tm == &t);
|
||||
assert(tm.copy_count == 0);
|
||||
assert(tm.move_count == 0);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove&) const> f = lambda;
|
||||
f(t);
|
||||
}
|
||||
{
|
||||
// Arg type is an rvalue reference, we should not copy or move the object
|
||||
TrackCopyMove t;
|
||||
auto lambda = [&t](TrackCopyMove&& tm) {
|
||||
assert(&tm == &t);
|
||||
assert(tm.copy_count == 0);
|
||||
assert(tm.move_count == 0);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove&&) const> f = lambda;
|
||||
f(std::move(t));
|
||||
}
|
||||
{
|
||||
// Arg type is a prvalue, we should move but not copy the object
|
||||
// In libc++, where the type is not trivially copyable, the object should be
|
||||
// moved exactly once when passing into the lambda. The internal functions
|
||||
// of function_ref should forward the argument without copying or moving it
|
||||
auto lambda = [](TrackCopyMove tm) {
|
||||
assert(tm.copy_count == 0);
|
||||
LIBCPP_ASSERT(tm.move_count == 1);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove) const> f = lambda;
|
||||
f(TrackCopyMove{});
|
||||
}
|
||||
}
|
||||
|
||||
void test_noexcept() {
|
||||
{
|
||||
std::function_ref<void() noexcept> f = [] noexcept {};
|
||||
f();
|
||||
static_assert(std::is_void_v<decltype(f())>);
|
||||
}
|
||||
{
|
||||
// reference
|
||||
int x = 42;
|
||||
std::function_ref<int&(int&) noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
|
||||
std::same_as<int&> decltype(auto) r = f(x);
|
||||
assert(&r == &x);
|
||||
}
|
||||
{
|
||||
// Move only
|
||||
std::function_ref<MoveOnly(MoveOnly) noexcept> f(
|
||||
std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
|
||||
std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
|
||||
assert(r.get() == 6);
|
||||
}
|
||||
{
|
||||
// different return type
|
||||
std::function_ref<int(int) noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
|
||||
std::same_as<int> decltype(auto) r = f(5);
|
||||
assert(r == 5);
|
||||
}
|
||||
{
|
||||
// Args overload resolution
|
||||
S s;
|
||||
int x = 1;
|
||||
std::function_ref<int(int&) noexcept> f(s);
|
||||
assert(f(x) == 43);
|
||||
|
||||
std::function_ref<int(const int&) noexcept> g(s);
|
||||
assert(g(x) == 44);
|
||||
|
||||
std::function_ref<int(int&&) noexcept> h(s);
|
||||
assert(h(std::move(x)) == 45);
|
||||
|
||||
std::function_ref<int(const int&&) noexcept> i(s);
|
||||
assert(i(std::move(x)) == 46);
|
||||
}
|
||||
{
|
||||
// const overload
|
||||
S2 s;
|
||||
std::function_ref<double(int, int, int) noexcept> f(s);
|
||||
std::same_as<double> decltype(auto) r = f(1, 2, 3);
|
||||
assert(r == 6.0);
|
||||
|
||||
std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
|
||||
assert(r2 == 6.0);
|
||||
|
||||
const S2 s2;
|
||||
std::function_ref<double(int, int, int) noexcept> f2(s2);
|
||||
std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
|
||||
assert(r3 == 7.0);
|
||||
|
||||
std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
|
||||
assert(r4 == 7.0);
|
||||
}
|
||||
{
|
||||
// Big object passed
|
||||
Big b{};
|
||||
b.c[0] = 'a';
|
||||
std::function_ref<char(Big&) noexcept> f1(std::cw<S3Ref{}>);
|
||||
assert(f1(b) == 'a');
|
||||
std::function_ref<char(Big) noexcept> f2(std::cw<S3Value{}>);
|
||||
assert(f2(b) == 'a');
|
||||
}
|
||||
{
|
||||
// Arg type is an lvalue reference, we should not copy or move the object
|
||||
TrackCopyMove t;
|
||||
auto lambda = [&t](TrackCopyMove& tm) noexcept {
|
||||
assert(&tm == &t);
|
||||
assert(tm.copy_count == 0);
|
||||
assert(tm.move_count == 0);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove&) noexcept> f = lambda;
|
||||
f(t);
|
||||
}
|
||||
{
|
||||
// Arg type is an rvalue reference, we should not copy or move the object
|
||||
TrackCopyMove t;
|
||||
auto lambda = [&t](TrackCopyMove&& tm) noexcept {
|
||||
assert(&tm == &t);
|
||||
assert(tm.copy_count == 0);
|
||||
assert(tm.move_count == 0);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove&&) noexcept> f = lambda;
|
||||
f(std::move(t));
|
||||
}
|
||||
{
|
||||
// Arg type is a prvalue, we should move but not copy the object
|
||||
// In libc++, where the type is not trivially copyable, the object should be
|
||||
// moved exactly once when passing into the lambda. The internal functions
|
||||
// of function_ref should forward the argument without copying or moving it
|
||||
auto lambda = [](TrackCopyMove tm) noexcept {
|
||||
assert(tm.copy_count == 0);
|
||||
LIBCPP_ASSERT(tm.move_count == 1);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove) noexcept> f = lambda;
|
||||
f(TrackCopyMove{});
|
||||
}
|
||||
}
|
||||
|
||||
void test_const_noexcept() {
|
||||
{
|
||||
std::function_ref<void() const noexcept> f = [] noexcept {};
|
||||
f();
|
||||
static_assert(std::is_void_v<decltype(f())>);
|
||||
}
|
||||
{
|
||||
// reference
|
||||
int x = 42;
|
||||
std::function_ref<int&(int&) const noexcept> f(std::cw<[](int& i) noexcept -> int& { return i; }>);
|
||||
std::same_as<int&> decltype(auto) r = f(x);
|
||||
assert(&r == &x);
|
||||
}
|
||||
{
|
||||
// Move only
|
||||
std::function_ref<MoveOnly(MoveOnly) const noexcept> f(
|
||||
std::cw<[](MoveOnly mo) noexcept { return MoveOnly{mo.get() + 5}; }>);
|
||||
std::same_as<MoveOnly> decltype(auto) r = f(MoveOnly{1});
|
||||
assert(r.get() == 6);
|
||||
}
|
||||
{
|
||||
// different return type
|
||||
std::function_ref<int(int) const noexcept> f(std::cw<[](int x) noexcept -> double { return x + 0.1; }>);
|
||||
std::same_as<int> decltype(auto) r = f(5);
|
||||
assert(r == 5);
|
||||
}
|
||||
{
|
||||
// Args overload resolution
|
||||
S s;
|
||||
int x = 1;
|
||||
std::function_ref<int(int&) const noexcept> f(s);
|
||||
assert(f(x) == 43);
|
||||
|
||||
std::function_ref<int(const int&) const noexcept> g(s);
|
||||
assert(g(x) == 44);
|
||||
|
||||
std::function_ref<int(int&&) const noexcept> h(s);
|
||||
assert(h(std::move(x)) == 45);
|
||||
|
||||
std::function_ref<int(const int&&) const noexcept> i(s);
|
||||
assert(i(std::move(x)) == 46);
|
||||
}
|
||||
{
|
||||
// const overload
|
||||
S2 s;
|
||||
std::function_ref<double(int, int, int) const noexcept> f(s);
|
||||
std::same_as<double> decltype(auto) r = f(1, 2, 3);
|
||||
assert(r == 7.0);
|
||||
|
||||
std::same_as<double> decltype(auto) r2 = std::as_const(f)(1, 2, 3);
|
||||
assert(r2 == 7.0);
|
||||
|
||||
const S2 s2;
|
||||
std::function_ref<double(int, int, int) const noexcept> f2(s2);
|
||||
std::same_as<double> decltype(auto) r3 = f2(1, 2, 3);
|
||||
assert(r3 == 7.0);
|
||||
|
||||
std::same_as<double> decltype(auto) r4 = std::as_const(f2)(1, 2, 3);
|
||||
assert(r4 == 7.0);
|
||||
}
|
||||
{
|
||||
// Big object passed
|
||||
Big b{};
|
||||
b.c[0] = 'a';
|
||||
std::function_ref<char(Big&) const noexcept> f1(std::cw<S3Ref{}>);
|
||||
assert(f1(b) == 'a');
|
||||
std::function_ref<char(Big) const noexcept> f2(std::cw<S3Value{}>);
|
||||
assert(f2(b) == 'a');
|
||||
}
|
||||
{
|
||||
// Arg type is an lvalue reference, we should not copy or move the object
|
||||
TrackCopyMove t;
|
||||
auto lambda = [&t](TrackCopyMove& tm) noexcept {
|
||||
assert(&tm == &t);
|
||||
assert(tm.copy_count == 0);
|
||||
assert(tm.move_count == 0);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove&) const noexcept> f = lambda;
|
||||
f(t);
|
||||
}
|
||||
{
|
||||
// Arg type is an rvalue reference, we should not copy or move the object
|
||||
TrackCopyMove t;
|
||||
auto lambda = [&t](TrackCopyMove&& tm) noexcept {
|
||||
assert(&tm == &t);
|
||||
assert(tm.copy_count == 0);
|
||||
assert(tm.move_count == 0);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove&&) const noexcept> f = lambda;
|
||||
f(std::move(t));
|
||||
}
|
||||
{
|
||||
// Arg type is a prvalue, we should move but not copy the object
|
||||
// In libc++, where the type is not trivially copyable, the object should be
|
||||
// moved exactly once when passing into the lambda. The internal functions
|
||||
// of function_ref should forward the argument without copying or moving it
|
||||
auto lambda = [](TrackCopyMove tm) noexcept {
|
||||
assert(tm.copy_count == 0);
|
||||
LIBCPP_ASSERT(tm.move_count == 1);
|
||||
};
|
||||
std::function_ref<void(TrackCopyMove) const noexcept> f = lambda;
|
||||
f(TrackCopyMove{});
|
||||
}
|
||||
}
|
||||
|
||||
void test() {
|
||||
test_default();
|
||||
test_const();
|
||||
test_noexcept();
|
||||
test_const_noexcept();
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// REQUIRES: std-at-least-c++26
|
||||
|
||||
// Each specialization of function_ref is a trivially copyable type ([basic.types.general]) that models copyable.
|
||||
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
struct A {
|
||||
A() {}
|
||||
A(const A&) {}
|
||||
~A() {}
|
||||
};
|
||||
|
||||
static_assert(std::copyable<std::function_ref<A(const A&)>>);
|
||||
static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&)>>);
|
||||
|
||||
static_assert(std::copyable<std::function_ref<A(const A&) noexcept>>);
|
||||
static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) noexcept>>);
|
||||
|
||||
static_assert(std::copyable<std::function_ref<A(const A&) const>>);
|
||||
static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const>>);
|
||||
|
||||
static_assert(std::copyable<std::function_ref<A(const A&) const noexcept>>);
|
||||
static_assert(std::is_trivially_copyable_v<std::function_ref<A(const A&) const noexcept>>);
|
||||
|
|
@ -707,10 +707,9 @@ feature_test_macros = [
|
|||
{
|
||||
"name": "__cpp_lib_function_ref",
|
||||
"values": {
|
||||
"c++26": 202306 # P0792R14 function_ref: a type-erased callable reference
|
||||
"c++26": 202603 # P3948R1 constant_wrapper is the only tool needed for passing constant expressions via function arguments
|
||||
},
|
||||
"headers": ["functional"],
|
||||
"unimplemented": True,
|
||||
},
|
||||
{
|
||||
"name": "__cpp_lib_gcd_lcm",
|
||||
|
|
|
|||
Loading…
Reference in a new issue