Added rvalue and const rvalue overloads to Try
authorAaryaman Sagar <aary@instagram.com>
Thu, 24 Aug 2017 21:46:17 +0000 (14:46 -0700)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Thu, 24 Aug 2017 21:52:36 +0000 (14:52 -0700)
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
folly/Try.h
folly/test/TryTest.cpp

index e70fce72c8d807e50ad3beb79732d50ab0e68dc1..711c1a0905278f3d56a705563604a86fc6f51d50 100644 (file)
@@ -117,6 +117,12 @@ const T& Try<T>::value() const & {
   return value_;
 }
 
+template <class T>
+const T&& Try<T>::value() const && {
+  throwIfFailed();
+  return std::move(value_);
+}
+
 template <class T>
 void Try<T>::throwIfFailed() const {
   switch (contains_) {
index efcc5a9749d2757efe14eb3d01118f7fc4632e98..7b97754c7d13a632220da91101c8ddd413bd1e68 100644 (file)
@@ -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
index 8e675f1f7738d7b6d3945759009c8a1870aa811c..7e16ee99ac12f6e7de0294cb9b65becbf67a680c 100644 (file)
@@ -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<int>(1);
+  auto t = Try<std::unique_ptr<int>>{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<MutableContainer> t{in_place};
+    auto val = MoveConstructOnly{std::move(t).value().val};
+    static_cast<void>(val);
+  }
+  {
+    const Try<MutableContainer> t{in_place};
+    auto val = (*(std::move(t))).val;
+    static_cast<void>(val);
+  }
+}
+
+TEST(Try, ValueOverloads) {
+  using ML = int&;
+  using MR = int&&;
+  using CL = const int&;
+  using CR = const int&&;
+
+  {
+    auto obj = Try<int>{};
+    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<ML, ActualML>::value));
+    EXPECT_TRUE((std::is_same<MR, ActualMR>::value));
+    EXPECT_TRUE((std::is_same<CL, ActualCL>::value));
+    EXPECT_TRUE((std::is_same<CR, ActualCR>::value));
+  }
+
+  {
+    auto obj = Try<int>{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<int>{make_exception_wrapper<std::range_error>("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<int> t;