Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions include/stdx/optional.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -318,21 +318,38 @@ template <typename T, typename TS = tombstone_traits<T>> class optional {

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

template <typename F, typename... Ts,
namespace detail {
template <typename T>
constexpr bool optional_like =
stdx::is_specialization_of_v<stdx::remove_cvref_t<T>, optional> or
stdx::is_specialization_of_v<stdx::remove_cvref_t<T>, std::optional>;

template <typename R, typename... Ts,
typename = std::enable_if_t<
(... and stdx::is_specialization_of_v<stdx::remove_cvref_t<Ts>,
optional>)>>
auto convert_optional(Ts const &...) -> optional<R>;
template <typename R, typename... Ts,
typename = std::enable_if_t<
(... and stdx::is_specialization_of_v<stdx::remove_cvref_t<Ts>,
std::optional>)>>
auto convert_optional(Ts const &...) -> std::optional<R>;
} // namespace detail

template <typename F, typename... Ts,
typename = std::enable_if_t<(... and detail::optional_like<Ts>)>>
constexpr auto transform(F &&f, Ts &&...ts) {
using func_t = stdx::remove_cvref_t<F>;
using R = std::invoke_result_t<
func_t,
forward_like_t<Ts, typename stdx::remove_cvref_t<Ts>::value_type>...>;
using O = decltype(detail::convert_optional<R>(ts...));
if ((... and ts.has_value())) {
return optional<R>{with_result_of{[&] {
return O{with_result_of{[&] {
return std::forward<F>(f)(std::forward<Ts>(ts).value()...);
}}};
}
return optional<R>{};
return O{};
}
} // namespace v1
} // namespace stdx
Expand Down
17 changes: 17 additions & 0 deletions test/optional.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,16 @@ TEST_CASE("transform (multi-arg)", "[optional]") {
auto o3 =
transform([](S &x, S &y) { return S{x.value + y.value}; }, o1, o2);
CHECK(o3->value == 59);
STATIC_REQUIRE(std::is_same_v<decltype(o3), stdx::optional<S>>);
}

TEST_CASE("multi-arg transform works on std::optional", "[optional]") {
auto o1 = std::optional<S>{17};
auto o2 = std::optional<S>{42};
auto o3 = stdx::transform([](S &x, S &y) { return S{x.value + y.value}; },
o1, o2);
CHECK(o3->value == 59);
STATIC_REQUIRE(std::is_same_v<decltype(o3), std::optional<S>>);
}

namespace {
Expand Down Expand Up @@ -427,6 +437,13 @@ TEST_CASE("transform (multi-arg nonmovable)", "[optional]") {
CHECK(o3->value == 59);
}

TEST_CASE("transform (non-default tombstone)", "[optional]") {
constexpr auto o1 = stdx::optional<int, stdx::tombstone_value<-1>>{17};
constexpr auto o2 = transform([](auto x) { return S{x}; }, o1);
STATIC_REQUIRE(o2);
STATIC_REQUIRE(o2->value == 17);
}

#if __cpp_nontype_template_args >= 201911L
TEST_CASE("tombstone with non-structural value", "[optional]") {
constexpr auto ts_value = CX_VALUE(std::string_view{});
Expand Down