Commit 382b70ad authored by Eric Niebler's avatar Eric Niebler Committed by Facebook Github Bot

Remove unneccessary test for &that==this in folly::Function's move assignment operator.

Summary: Self-moves are exceedingly rare and need not preserve the state of the object. They must only leave the object in a valid but unspecified state. By removing the branch, we make the common case (non-self move) faster.

Reviewed By: spacedentist, ot

Differential Revision: D4803486

fbshipit-source-id: 3ef2e1e13cd08d9221ecb154bfb3338b16487717
parent 72ea700c
......@@ -578,17 +578,23 @@ class Function final : private detail::function::FunctionTraits<FunctionType> {
/**
* Move assignment operator
*
* \note Leaves `that` in a valid but unspecified state. If `&that == this`
* then `*this` is left in a valid but unspecified state.
*/
Function& operator=(Function&& that) noexcept {
if (&that != this) {
// Q: Why is is safe to destroy and reconstruct this object in place?
// A: Two reasons: First, `Function` is a final class, so in doing this
// we aren't slicing off any derived parts. And second, the move
// operation is guaranteed not to throw so we always leave the object
// in a valid state.
this->~Function();
::new (this) Function(std::move(that));
}
// Q: Why is is safe to destroy and reconstruct this object in place?
// A: Two reasons: First, `Function` is a final class, so in doing this
// we aren't slicing off any derived parts. And second, the move
// operation is guaranteed not to throw so we always leave the object
// in a valid state.
// In the case of self-move (this == &that), this leaves the object in
// a default-constructed state. First the object is destroyed, then we
// pass the destroyed object to the move constructor. The first thing the
// move constructor does is default-construct the object. That object is
// "moved" into itself, which is a no-op for a default-constructed Function.
this->~Function();
::new (this) Function(std::move(that));
return *this;
}
......
......@@ -1045,11 +1045,27 @@ TEST(Function, EmptyAfterConstCast) {
EXPECT_FALSE(func2);
}
TEST(Function, SelfMoveAssign) {
Function<int()> f = [] { return 0; };
TEST(Function, SelfStdSwap) {
Function<int()> f = [] { return 42; };
f.swap(f);
EXPECT_TRUE(bool(f));
EXPECT_EQ(42, f());
std::swap(f, f);
EXPECT_TRUE(bool(f));
EXPECT_EQ(42, f());
folly::swap(f, f);
EXPECT_TRUE(bool(f));
EXPECT_EQ(42, f());
}
TEST(Function, SelfMove) {
Function<int()> f = [] { return 42; };
Function<int()>& g = f;
f = std::move(g);
f = std::move(g); // shouldn't crash!
(void)bool(f); // valid but unspecified state
f = [] { return 43; };
EXPECT_TRUE(bool(f));
EXPECT_EQ(43, f());
}
TEST(Function, DeducableArguments) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment