3

我有一个返回可选结构的方法,如下所示:

auto getBook(const std::string &title) const -> std::optional<Book>;

我想在另一个返回可选作者的方法中调用此方法。问题是实现应该始终检查 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();
}
4

4 回答 4

8

您可以通过创建一个map函数来概括此模式,该函数接受一个 optionalo和一个 function f,并返回f(*o)if的结果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;
    });
}

godbolt.org 上的实时示例


我为这类操作创建了一个库,称为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 可选操作”

于 2020-10-23T12:47:54.630 回答
0

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));
}
于 2020-10-23T13:21:07.587 回答
0

我认为,这里需要空对象模式而不是 std::optional 。

是的,它有点像 std::optional ,但语义不同:当对对象调用任何方法(如 getAuthor)时,如果方法的结果有效,则返回有效对象。如果不是,则返回 null 对象。

不久前,我研究了一个类似 XML 的树实现,它是这样的:

auto val = object.node("root").node("branch_1").node("subbranch_1").toInt();
if (val) process(val.asInt());

在 node() 调用的每一步,如果未找到特定节点,则返回无效节点。在无效节点上调用时,返回无效节点。

AFAIK,目前在 STL 中没有实现类似的功能。

编辑:为了有效地工作,您需要从某个基类派生书籍、作者、章节和其他内容的所有实例,空对象将属于该基类。

编辑:这不是所有情况下的最佳解决方案,可能这太复杂而无法在您的情况下实施。

于 2020-10-23T13:22:12.827 回答
-1

我认为您可以只返回您想要的内容并在根函数中捕获异常,如下所示:

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 (...)
{
}

于 2020-10-23T13:02:48.460 回答