Skip to content

Commit 0101a0c

Browse files
committed
🎨 Make multi-arg transform work on std::optional
Problem: - Sometimes we want to use `std::optional` (not just `stdx::optional`) as an applicative functor. Solution: - Make multi-arg `transform` work on `std::optional` as well as `stdx::optional`.
1 parent ca9e67a commit 0101a0c

File tree

2 files changed

+24
-5
lines changed

2 files changed

+24
-5
lines changed

include/stdx/optional.hpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -318,21 +318,30 @@ template <typename T, typename TS = tombstone_traits<T>> class optional {
318318

319319
template <typename T> optional(T) -> optional<T>;
320320

321+
namespace detail {
322+
template <typename T>
323+
constexpr bool optional_like =
324+
stdx::is_specialization_of_v<stdx::remove_cvref_t<T>, optional> or
325+
stdx::is_specialization_of_v<stdx::remove_cvref_t<T>, std::optional>;
326+
327+
template <typename R, template <typename> typename O, typename... Ts>
328+
auto convert_optional(O<Ts> const &...) -> O<R>;
329+
} // namespace detail
330+
321331
template <typename F, typename... Ts,
322-
typename = std::enable_if_t<
323-
(... and stdx::is_specialization_of_v<stdx::remove_cvref_t<Ts>,
324-
optional>)>>
332+
typename = std::enable_if_t<(... and detail::optional_like<Ts>)>>
325333
constexpr auto transform(F &&f, Ts &&...ts) {
326334
using func_t = stdx::remove_cvref_t<F>;
327335
using R = std::invoke_result_t<
328336
func_t,
329337
forward_like_t<Ts, typename stdx::remove_cvref_t<Ts>::value_type>...>;
338+
using O = decltype(detail::convert_optional<R>(ts...));
330339
if ((... and ts.has_value())) {
331-
return optional<R>{with_result_of{[&] {
340+
return O{with_result_of{[&] {
332341
return std::forward<F>(f)(std::forward<Ts>(ts).value()...);
333342
}}};
334343
}
335-
return optional<R>{};
344+
return O{};
336345
}
337346
} // namespace v1
338347
} // namespace stdx

test/optional.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,16 @@ TEST_CASE("transform (multi-arg)", "[optional]") {
334334
auto o3 =
335335
transform([](S &x, S &y) { return S{x.value + y.value}; }, o1, o2);
336336
CHECK(o3->value == 59);
337+
STATIC_REQUIRE(std::is_same_v<decltype(o3), stdx::optional<S>>);
338+
}
339+
340+
TEST_CASE("multi-arg transform works on std::optional", "[optional]") {
341+
auto o1 = std::optional<S>{17};
342+
auto o2 = std::optional<S>{42};
343+
auto o3 = stdx::transform([](S &x, S &y) { return S{x.value + y.value}; },
344+
o1, o2);
345+
CHECK(o3->value == 59);
346+
STATIC_REQUIRE(std::is_same_v<decltype(o3), std::optional<S>>);
337347
}
338348

339349
namespace {

0 commit comments

Comments
 (0)