类型hana::optional表示
可选值,其可选性在编译时已知。
除其他外,它还模拟了Monad的概念。因为它是编译时可选的,所以如果我尝试在运行时中选择一个非空的hana::optional (没有包装另一个hana::optional ),那么在运行时会发生什么问题是没有意义的,因为这个错误会在编译时被捕获。
但是运行时间的可选性呢?
下面是我试图使std::optional (而且只有它,我没有提出一个可能的一般概念,这也是boost::optional可以满足的) hana::Monad,我想知道这是否是正确的方法:
#include <assert.h>
#include <boost/hana/chain.hpp>
#include <optional>
#include <stdexcept>
#include <type_traits>
namespace hana = boost::hana;
template<typename T, typename = void>
struct has_value_type : std::false_type {};
template<typename T>
struct has_value_type<T, std::void_t<typename T::value_type>> : std::true_type {};
template<typename T>
bool constexpr has_value_type_t = has_value_type<T>::value;
namespace boost::hana {
template<typename T>
struct transform_impl<std::optional<T>> {
template <typename Opt, typename F>
static constexpr auto apply(Opt&& o, F&& f) {
return o.has_value() ? std::make_optional(f(o.value())) : std::nullopt;
}
};
template<typename T>
struct flatten_impl<std::optional<T>> {
template <typename X>
static constexpr auto apply(X&& x) {
if constexpr (has_value_type_t<typename X::value_type>)
return (x.has_value() && x.value().has_value()) ? x.value() : std::nullopt;
else {
if (!x.has_value())
return std::nullopt;
else
throw std::runtime_error("std::optional<non_stdoptional_type> can't be flattened!");
}
}
};
template<>
struct flatten_impl<std::nullopt_t> {
template <typename X>
static constexpr auto apply(X&&) {
return std::nullopt;
}
};
}
int main() {
assert(hana::flatten(std::optional<std::optional<int>>{3}) == std::optional<int>{3});
assert(hana::flatten(std::optional<int>{}) == std::optional<int>{});
assert(hana::flatten(std::nullopt) == std::optional<int>{});
//assert(hana::flatten(std::optional<int>{2}) == std::optional<int>{}); // throws
}发布于 2021-02-15 20:36:21
有一些改进是可以做的。
Boost.Hana使用标记分派系统来专门化impl结构。这简化了专门化,但也防止了外部impl结构与内部函数模板实例化的笛卡儿积。
而且,如果您使用value_type来检测嵌套的可选选项,考虑到满足此约束的类型范围很广,则会出现错误。如果您使用hana::is_a<optional_tag, X>,标记系统也会在这方面有所帮助。
这个更具体的约束也消除了对嵌套可选项执行运行时检查的需要,因为信息本身就在类型中。optional<optional<...>>
专门用于flatten_impl<nullopt_t>是不正确的,因为您不能通过transform运行它。我不认为这是必要的。
下面是一个简化的版本,它还演示了您可以转换类型。也是警员!
#include <assert.h>
#include <boost/hana/core/is_a.hpp>
#include <boost/hana/core/tag_of.hpp>
#include <boost/hana/flatten.hpp>
#include <optional>
#include <string>
namespace hana = boost::hana;
struct my_optional_tag { };
namespace boost::hana {
template <typename T>
struct tag_of<std::optional<T>> {
using type = my_optional_tag;
};
template <>
struct transform_impl<my_optional_tag> {
template <typename Opt, typename F>
static constexpr auto apply(Opt&& o, F&& f) {
using ResultValue = decltype(std::forward<F>(f)(std::forward<Opt>(o).value()));
using Result = std::optional<std::decay_t<ResultValue>>;
return o.has_value() ? Result(std::forward<F>(f)(std::forward<Opt>(o).value())) :
Result(std::nullopt);
}
};
template <>
struct flatten_impl<my_optional_tag> {
template <typename X>
static constexpr auto apply(X&& x) {
using Nested = decltype(std::forward<X>(x).value());
static_assert(is_a<my_optional_tag, Nested>, "must have nested optional");
return std::forward<X>(x).value_or(Nested(std::nullopt));
}
};
}
int main() {
// Functor
static_assert(hana::transform(std::optional<int>{41}, [](int x) { return x + 1.0f; }) ==
std::optional<float>{42});
// Monad
static_assert(hana::flatten(std::optional<std::optional<int>>{3}) == std::optional<int>{3});
constexpr auto inc = [](int x) { return std::optional{x + 1.0f}; };
static_assert(hana::chain(std::optional<int>{5}, inc) == std::optional{6.0f});
static_assert(hana::flatten(std::optional<std::optional<int>>{std::nullopt}) == std::nullopt);
// hana::flatten(std::optional<int>{3}); // compile-time error
}请注意,我忽略了hana::lift和hana::ap的实现,这两个实现是hana::Applicative所必需的,后者是hana::Monad所必需的(在文档化的最小完全定义中)。我会把这个留给读者。
https://stackoverflow.com/questions/66197453
复制相似问题