将一个 std::optional 转换为另一个 std::optional
Converting one std::optional to another std::optional
我有一个方法 returns 一个可选的结构,像这样:
auto getBook(const std::string &title) const -> std::optional<Book>;
我想在 returns 可选作者的另一个方法中调用此方法。
问题是在调用方法之前,实现应该始终检查 getBook 返回的可选值是否已填写,如下所示:
auto getAuthor(const std::string &title) const -> std::optional<Author>
{
const auto optBook = getBook(title);
if (optBook.has_value)
return optBook->getAuthor();
else
return std::nullopt;
}
有没有一种方法可以用更短的方式来写这个,这样如果可选项被填充,则调用方法,但如果可选项为空,则返回 std::nullopt
。像这样的东西(我知道这目前不起作用,但你明白我的意思):
auto getAuthor(const std::string &title) const -> std::optional<Author>
{
return getBook(title).getAuthor();
}
您可以通过创建一个 map
函数来概括此模式,该函数接受一个可选的 o
和一个函数 f
,以及 returns [=16] 的结果=] 如果 o.has_value() == true
:
template <typename O, typename F>
auto map(O&& o, F&& f) -> std::optional<decltype(f(*std::forward<O>(o)))>
{
if (!o.has_value())
{
return {std::nullopt};
}
return {f(*std::forward<O>(o))};
}
然后您可以将 getAuthor
定义为:
auto getAuthor(const std::string& title) -> std::optional<Author>
{
return map(getBook(title), [](Book b)
{
return b.author;
});
}
我为这些操作创建了一个库,名为 scelta
。使用我的库,你可以写:
auto getBook(const std::string& title) -> std::optional<Book>;
auto getAuthor(const std::optional<Book>& title) -> std::optional<Author>;
using namespace scelta::infix;
std::optional<Author> a = getBook("...") | map(getAuthor);
有关详细信息,请参阅 "Monadic Optional Operations"。
我想你可以 return 你想要的并在根函数中捕获异常,像这样:
auto get_size_t(const bool state)
{
return state ? std::optional<size_t>(std::rand()) : std::nullopt;
}
auto get_string_size_t(const bool state)
{
return std::optional<std::string>(std::to_string(get_size_t(state).value()));
}
void f()
try
{
std::clog << get_string_size_t(true).value() << std::endl;
std::clog << get_string_size_t(false).value() << std::endl;
}
catch (...)
{
}
boost::optional
,这是 std::optional
的基础,有一个成员 map
可以做到这一点。
例如
auto getBook(const std::string &title) const -> boost::optional<Book>;
auto getAuthor(const std::string &title) const -> boost::optional<Author>
{
return getBook(title).map(std::mem_fn(&Book::getAuthor));
}
我认为,此处需要 Null Object 模式而不是 std::optional。
是的,有点像 std::optional,但语义不同:
当在对象上调用任何方法(如 getAuthor)时,如果方法的结果有效,则返回有效对象。如果不是,则返回 null 对象。
不久前,我在研究 XML-like 树实现,它是这样的:
auto val = object.node("root").node("branch_1").node("subbranch_1").toInt();
if (val) process(val.asInt());
在调用 node() 的每一步中,如果未找到特定节点,则返回无效节点。在无效节点上调用时,返回无效节点。
据我所知,目前在 STL 中没有实现类似的东西。
编辑:为了使其有效工作,您需要所有书籍、作者、章节和其他内容的实例都派生自某个基础 class,空对象将属于该基础。
编辑:这不是适用于所有情况的最佳解决方案,可能在您的情况下实施起来太复杂了。
我有一个方法 returns 一个可选的结构,像这样:
auto getBook(const std::string &title) const -> std::optional<Book>;
我想在 returns 可选作者的另一个方法中调用此方法。 问题是在调用方法之前,实现应该始终检查 getBook 返回的可选值是否已填写,如下所示:
auto getAuthor(const std::string &title) const -> std::optional<Author>
{
const auto optBook = getBook(title);
if (optBook.has_value)
return optBook->getAuthor();
else
return std::nullopt;
}
有没有一种方法可以用更短的方式来写这个,这样如果可选项被填充,则调用方法,但如果可选项为空,则返回 std::nullopt
。像这样的东西(我知道这目前不起作用,但你明白我的意思):
auto getAuthor(const std::string &title) const -> std::optional<Author>
{
return getBook(title).getAuthor();
}
您可以通过创建一个 map
函数来概括此模式,该函数接受一个可选的 o
和一个函数 f
,以及 returns [=16] 的结果=] 如果 o.has_value() == true
:
template <typename O, typename F>
auto map(O&& o, F&& f) -> std::optional<decltype(f(*std::forward<O>(o)))>
{
if (!o.has_value())
{
return {std::nullopt};
}
return {f(*std::forward<O>(o))};
}
然后您可以将 getAuthor
定义为:
auto getAuthor(const std::string& title) -> std::optional<Author>
{
return map(getBook(title), [](Book b)
{
return b.author;
});
}
我为这些操作创建了一个库,名为 scelta
。使用我的库,你可以写:
auto getBook(const std::string& title) -> std::optional<Book>;
auto getAuthor(const std::optional<Book>& title) -> std::optional<Author>;
using namespace scelta::infix;
std::optional<Author> a = getBook("...") | map(getAuthor);
有关详细信息,请参阅 "Monadic Optional Operations"。
我想你可以 return 你想要的并在根函数中捕获异常,像这样:
auto get_size_t(const bool state)
{
return state ? std::optional<size_t>(std::rand()) : std::nullopt;
}
auto get_string_size_t(const bool state)
{
return std::optional<std::string>(std::to_string(get_size_t(state).value()));
}
void f()
try
{
std::clog << get_string_size_t(true).value() << std::endl;
std::clog << get_string_size_t(false).value() << std::endl;
}
catch (...)
{
}
boost::optional
,这是 std::optional
的基础,有一个成员 map
可以做到这一点。
例如
auto getBook(const std::string &title) const -> boost::optional<Book>;
auto getAuthor(const std::string &title) const -> boost::optional<Author>
{
return getBook(title).map(std::mem_fn(&Book::getAuthor));
}
我认为,此处需要 Null Object 模式而不是 std::optional。
是的,有点像 std::optional,但语义不同: 当在对象上调用任何方法(如 getAuthor)时,如果方法的结果有效,则返回有效对象。如果不是,则返回 null 对象。
不久前,我在研究 XML-like 树实现,它是这样的:
auto val = object.node("root").node("branch_1").node("subbranch_1").toInt();
if (val) process(val.asInt());
在调用 node() 的每一步中,如果未找到特定节点,则返回无效节点。在无效节点上调用时,返回无效节点。
据我所知,目前在 STL 中没有实现类似的东西。
编辑:为了使其有效工作,您需要所有书籍、作者、章节和其他内容的实例都派生自某个基础 class,空对象将属于该基础。
编辑:这不是适用于所有情况的最佳解决方案,可能在您的情况下实施起来太复杂了。