diff --git a/folly/Expected.h b/folly/Expected.h index d26895e9276..265c286e40c 100644 --- a/folly/Expected.h +++ b/folly/Expected.h @@ -962,6 +962,27 @@ class Expected final : expected_detail::ExpectedStorage { this->assign(std::move(that)); } + // Implicit then-chaining conversion: if `Expected` can be + // constructed from `V`, then we can directly convert `Expected` to + // `Expected`. + // + // Specifically, this allows a user-defined conversions of `V` to + // `Expected` to work as desired with range-based iteration + // over containers of `Expected`. + template < + class V, + class E FOLLY_REQUIRES_TRAILING( + !std::is_same, Expected>::value && + !std::is_constructible::value && + std::is_constructible, V&&>::value && + std::is_constructible::value)> + /* implicit */ Expected(Expected that) + : Base{expected_detail::EmptyTag{}} { + this->assign(std::move(that).then([](V&& v) -> Expected { + return Expected{v}; + })); + } + FOLLY_REQUIRES(std::is_copy_constructible::value) constexpr /* implicit */ Expected(const Value& val) noexcept( noexcept(Value(val))) diff --git a/folly/test/ExpectedTest.cpp b/folly/test/ExpectedTest.cpp index 226342438d6..a62b96a159e 100644 --- a/folly/test/ExpectedTest.cpp +++ b/folly/test/ExpectedTest.cpp @@ -974,4 +974,17 @@ TEST(Expected, TestUnique) { })); } +struct ConvertibleNum { + /*implicit*/ operator Expected() const { return num_; } + int num_; +}; + +TEST(Expected, TestChainedConversion) { + auto vs = + std::vector>{ConvertibleNum{.num_ = 137}}; + for (Expected v : vs) { + ASSERT_EQ(137, *v); + } +} + } // namespace folly