@@ -67,7 +67,11 @@ concept HasStaticRelease = requires {
6767};
6868
6969template <typename F, typename R, typename ... Args>
70- concept invocable_return = std::invocable<F, Args...> && std::same_as<std::invoke_result_t <F, Args...>, R>;
70+ concept invocable_return = std::invocable<F, Args...> && std::convertible_to<std::invoke_result_t <F, Args...>, R>;
71+
72+ template <typename F>
73+ concept scope_exit_function =
74+ invocable_return<F, void > && std::is_nothrow_move_constructible_v<F> && std::is_copy_constructible_v<F>;
7175
7276template <typename T>
7377concept scope_invoke_checker = HasStaticCanInvoke<T> || HasCanInvoke<T> || invocable_return<T, bool >;
@@ -78,19 +82,24 @@ struct ExecuteAlways;
7882
7983// =========================================================
8084
81- template <invocable_return< void > ExitFunc, scope_invoke_checker InvokeChecker = ExecuteAlways>
85+ template <scope_exit_function ExitFunc, scope_invoke_checker InvokeChecker = ExecuteAlways>
8286class scope_guard {
8387 public:
84- explicit scope_guard (ExitFunc&& exit_func) /* noexcept(see below)*/
85- : m_exit_func(std::move(exit_func)) //
86- {}
88+ explicit constexpr scope_guard (ExitFunc&& exit_func) noexcept (std::is_nothrow_constructible_v<ExitFunc>) //
89+ try
90+ : m_exit_func(std::forward<ExitFunc>(exit_func)) //
91+ {
92+ } catch (...) {
93+ m_exit_func ();
94+ }
8795
88- explicit scope_guard (ExitFunc&& exit_func, InvokeChecker&& invoke_checker) /* noexcept(see below)*/
89- : m_exit_func(std::move(exit_func)),
90- m_invoke_checker{std::move (invoke_checker)} //
96+ explicit constexpr scope_guard (ExitFunc&& exit_func, InvokeChecker&& invoke_checker) noexcept (
97+ std::is_nothrow_constructible_v<ExitFunc> && std::is_nothrow_constructible_v<InvokeChecker>)
98+ : m_exit_func(std::forward<ExitFunc>(exit_func)),
99+ m_invoke_checker{std::forward<InvokeChecker>(invoke_checker)} //
91100 {}
92101
93- explicit scope_guard (scope_guard&& rhs) noexcept
102+ explicit constexpr scope_guard (scope_guard&& rhs) noexcept
94103 : m_exit_func{std::move (rhs)},
95104 m_invoke_checker{std::move (rhs.m_invoke_checker )} //
96105 {}
@@ -99,13 +108,13 @@ class scope_guard {
99108 scope_guard& operator =(const scope_guard&) = delete ;
100109 scope_guard& operator =(scope_guard&&) = delete ;
101110
102- ~scope_guard () /* noexcept(see below) */ {
103- if (can_invoke_check (m_invoke_checker)) {
111+ constexpr ~scope_guard () noexcept (noexcept (m_exit_func())) {
112+ if (check_can_invoke (m_invoke_checker)) {
104113 m_exit_func ();
105114 }
106115 }
107116
108- void release () noexcept
117+ constexpr void release () noexcept // Shouldn't this noexcept be dependent on the noexcept of the release function?
109118 requires HasRelease<InvokeChecker> || HasStaticRelease<InvokeChecker>
110119 {
111120 if constexpr (HasRelease<InvokeChecker>) {
@@ -116,23 +125,17 @@ class scope_guard {
116125 }
117126
118127 private:
119- ExitFunc m_exit_func;
128+ [[no_unique_address]] ExitFunc m_exit_func;
120129 [[no_unique_address]] InvokeChecker m_invoke_checker;
121130
122131 template <typename T>
123- static bool can_invoke_check (const T& obj) {
132+ static constexpr bool check_can_invoke (const T& obj) {
124133 if constexpr (HasStaticCanInvoke<T>) {
125134 return T::can_invoke ();
126135 } else if constexpr (HasCanInvoke<T>) {
127136 return obj.can_invoke ();
128- } else if constexpr (invocable_return<T, bool >) {
129- return std::invoke (obj);
130- // } else if constexpr (HasStaticParenthesisOperator<T>) {
131- // return T::operator()();
132- // } else if constexpr (HasParenthesisOperator<T>) {
133- // return obj();
134137 } else {
135- return true ; // Default behavior if no check function is available
138+ return std::invoke (obj);
136139 }
137140 }
138141};
0 commit comments