Skip to content

Commit 7d49d4b

Browse files
authored
Clean up final_act and finally, closes #846 (#977)
Somewhere along the way, GSL's implementation of final_act and finally seems to have become way overthought. This PR is to re-simplify these facilities back to what C++ Core Guidelines C.30 said which is simple and clear and works. It just copies the invocable thing, and doesn't bother trying to optimize the copy. This should be fine, because we're typically passing something that's cheap to copy, often a stateless lambda. The problem in #846 appears to be because finally looks like was originally written as a const&/&& overload (its state at the time that issue was opened)... to eliminate a copy when you invoke it with a temporary. If so, then the && was probably never intended to be a forwarder, but an rvalue reference that tripped over the horrid C++ syntax collision where a && parameter magically instead means a forwarding reference because the type happens to be a template parameter type here. So I suspect the original author was just trying to write an rvalue overload, and the forwarder that's there now was never intended at all.
1 parent 849f7ce commit 7d49d4b

File tree

2 files changed

+22
-23
lines changed

2 files changed

+22
-23
lines changed

include/gsl/util

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -65,40 +65,29 @@ template <class F>
6565
class final_action
6666
{
6767
public:
68-
static_assert(!std::is_reference<F>::value && !std::is_const<F>::value &&
69-
!std::is_volatile<F>::value,
70-
"Final_action should store its callable by value");
68+
explicit final_action(const F& ff) noexcept : f{ff} { }
69+
explicit final_action(F&& ff) noexcept : f{std::move(ff)} { }
7170

72-
explicit final_action(F f) noexcept : f_(std::move(f)) {}
71+
~final_action() noexcept { if (invoke) f(); }
7372

7473
final_action(final_action&& other) noexcept
75-
: f_(std::move(other.f_)), invoke_(std::exchange(other.invoke_, false))
76-
{}
74+
: f(std::move(other.f)), invoke(std::exchange(other.invoke, false))
75+
{ }
7776

78-
final_action(const final_action&) = delete;
79-
final_action& operator=(const final_action&) = delete;
80-
final_action& operator=(final_action&&) = delete;
81-
82-
// clang-format off
83-
GSL_SUPPRESS(f.6) // NO-FORMAT: attribute // terminate if throws
84-
// clang-format on
85-
~final_action() noexcept
86-
{
87-
if (invoke_) f_();
88-
}
77+
final_action(const final_action&) = delete;
78+
void operator=(const final_action&) = delete;
79+
void operator=(final_action&&) = delete;
8980

9081
private:
91-
F f_;
92-
bool invoke_{true};
82+
F f;
83+
bool invoke = true;
9384
};
9485

9586
// finally() - convenience function to generate a final_action
9687
template <class F>
97-
GSL_NODISCARD final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>
98-
finally(F&& f) noexcept
88+
GSL_NODISCARD auto finally(F&& f) noexcept
9989
{
100-
return final_action<typename std::remove_cv<typename std::remove_reference<F>::type>::type>(
101-
std::forward<F>(f));
90+
return final_action<std::decay_t<F>>{std::forward<F>(f)};
10291
}
10392

10493
// narrow_cast(): a searchable way to do narrowing casts of values

tests/utils_tests.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,16 @@ TEST(utils_tests, finally_function_ptr)
112112
EXPECT_TRUE(j == 1);
113113
}
114114

115+
TEST(utils_tests, finally_function)
116+
{
117+
j = 0;
118+
{
119+
auto _ = finally(g);
120+
EXPECT_TRUE(j == 0);
121+
}
122+
EXPECT_TRUE(j == 1);
123+
}
124+
115125
TEST(utils_tests, narrow_cast)
116126
{
117127
int n = 120;

0 commit comments

Comments
 (0)