From 51647aaa0ac0eaf8364f6dd643e0f615cd256819 Mon Sep 17 00:00:00 2001 From: Aaryaman Sagar Date: Thu, 24 Aug 2017 14:46:17 -0700 Subject: [PATCH] Added rvalue and const rvalue overloads to Try Summary: Try was missing some important-ish overloads that help it behave well in rvalue contexts Reviewed By: yfeldblum Differential Revision: D5692021 fbshipit-source-id: c34627b56eb52dceaeb1f00ae930ee3bc6e00306 --- folly/Try-inl.h | 6 ++++ folly/Try.h | 39 ++++++++++++++++++++--- folly/test/TryTest.cpp | 70 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 5 deletions(-) diff --git a/folly/Try-inl.h b/folly/Try-inl.h index e70fce72..711c1a09 100644 --- a/folly/Try-inl.h +++ b/folly/Try-inl.h @@ -117,6 +117,12 @@ const T& Try::value() const & { return value_; } +template +const T&& Try::value() const && { + throwIfFailed(); + return std::move(value_); +} + template void Try::throwIfFailed() const { switch (contains_) { diff --git a/folly/Try.h b/folly/Try.h index efcc5a97..7b97754c 100644 --- a/folly/Try.h +++ b/folly/Try.h @@ -135,21 +135,28 @@ class Try { * * @returns mutable reference to the contained value */ - T& value()&; + T& value() &; /* * Get a rvalue reference to the contained value. If the Try contains an * exception it will be rethrown. * * @returns rvalue reference to the contained value */ - T&& value()&&; + T&& value() &&; /* * Get a const reference to the contained value. If the Try contains an * exception it will be rethrown. * * @returns const reference to the contained value */ - const T& value() const&; + const T& value() const &; + /* + * Get a const rvalue reference to the contained value. If the Try contains an + * exception it will be rethrown. + * + * @returns const rvalue reference to the contained value + */ + const T&& value() const &&; /* * If the Try contains an exception, rethrow it. Otherwise do nothing. @@ -162,13 +169,35 @@ class Try { * * @returns const reference to the contained value */ - const T& operator*() const { return value(); } + const T& operator*() const & { + return value(); + } /* * Dereference operator. If the Try contains an exception it will be rethrown. * * @returns mutable reference to the contained value */ - T& operator*() { return value(); } + T& operator*() & { + return value(); + } + /* + * Mutable rvalue dereference operator. If the Try contains an exception it + * will be rethrown. + * + * @returns rvalue reference to the contained value + */ + T&& operator*() && { + return std::move(value()); + } + /* + * Const rvalue dereference operator. If the Try contains an exception it + * will be rethrown. + * + * @returns rvalue reference to the contained value + */ + const T&& operator*() const && { + return std::move(value()); + } /* * Const arrow operator. If the Try contains an exception it will be diff --git a/folly/test/TryTest.cpp b/folly/test/TryTest.cpp index 8e675f1f..7e16ee99 100644 --- a/folly/test/TryTest.cpp +++ b/folly/test/TryTest.cpp @@ -36,6 +36,18 @@ class A { private: int x_; }; + +class MoveConstructOnly { + public: + MoveConstructOnly() = default; + MoveConstructOnly(const MoveConstructOnly&) = delete; + MoveConstructOnly(MoveConstructOnly&&) = default; +}; + +class MutableContainer { + public: + mutable MoveConstructOnly val; +}; } TEST(Try, basic) { @@ -59,6 +71,64 @@ TEST(Try, in_place_nested) { EXPECT_EQ(5, t_t_a.value().value().x()); } +TEST(Try, MoveDereference) { + auto ptr = std::make_unique(1); + auto t = Try>{std::move(ptr)}; + auto result = *std::move(t); + EXPECT_EQ(*result, 1); +} + +TEST(Try, MoveConstRvalue) { + // tests to see if Try returns a const Rvalue, this is required in the case + // where for example MutableContainer has a mutable memebr that is move only + // and you want to fetch the value from the Try and move it into a member + { + const Try t{in_place}; + auto val = MoveConstructOnly{std::move(t).value().val}; + static_cast(val); + } + { + const Try t{in_place}; + auto val = (*(std::move(t))).val; + static_cast(val); + } +} + +TEST(Try, ValueOverloads) { + using ML = int&; + using MR = int&&; + using CL = const int&; + using CR = const int&&; + + { + auto obj = Try{}; + using ActualML = decltype(obj.value()); + using ActualMR = decltype(std::move(obj).value()); + using ActualCL = decltype(as_const(obj).value()); + using ActualCR = decltype(std::move(as_const(obj)).value()); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + EXPECT_TRUE((std::is_same::value)); + } + + { + auto obj = Try{3}; + EXPECT_EQ(obj.value(), 3); + EXPECT_EQ(std::move(obj).value(), 3); + EXPECT_EQ(as_const(obj).value(), 3); + EXPECT_EQ(std::move(as_const(obj)).value(), 3); + } + + { + auto obj = Try{make_exception_wrapper("oops")}; + EXPECT_THROW(obj.value(), std::range_error); + EXPECT_THROW(std::move(obj.value()), std::range_error); + EXPECT_THROW(as_const(obj.value()), std::range_error); + EXPECT_THROW(std::move(as_const(obj.value())), std::range_error); + } +} + // Make sure we can copy Trys for copyable types TEST(Try, copy) { Try t; -- 2.34.1