(原文链接:https://abseil.io/tips/163 译者:clangpp@gmail.com)
每周贴士 #163: 传递absl::optional
参数
- 最初发布于:2019-07-11
- 作者:Ian Eldred Pudney
- 更新于:2020-04-06
- 短链接:abseil.io/tips/163
空值真的是十亿美元的错误吗?
问题
比方说你需要实现一个函数,接收一个可能存在也可能不存在的参数。你可能禁不住去用现代的,闪瞎狗眼的absl::optional
。但是吧,如果这个对象大到了应该传引用的程度,那也许absl::optional
并不是你真的想要的。考虑如下两个声明:
void MyFunc(const absl::optional<Foo>& foo); // 以值拷贝
void MyFunc(absl::optional<const Foo&> foo); // 编译错误
第一个选项做的可能跟你想象的不同。如果有人传了一个Foo
进MyFunc
,这个Foo
会被以值拷贝到absl::optional<Foo>
,然后以引用传进这个函数。如果你本来的目的是避免拷贝Foo
,那你所托非人了。
第二个选项相当不错,但不幸的是,absl::optional
不支持。
建议
如果你需要一个可能不存在的参数,以const *
传递之,并以nullptr
表示“不存在”。
void MyFunc(const Foo* foo);
这种方式跟传递const Foo&
一样高效,但它支持空值。
std::optional
的文档指出,你可以用std::reference_wrapper
来曲线救国,绕过可选的引用(optional reference)不被支持的事实:
void MyFunc(absl::optional<std::reference_wrapper<const Foo>> foo);
然而,这里的零碎儿太多了,而且读起来费劲。因此,我们不建议用它。
那absl::optional
到底有什么用?
当你拥有一个可选的东西的时候,absl::optional
就有用武之地了。例如,类成员和函数返回值通常跟absl::optional
搭档得很好。如果你不拥有一个可选的东西,那如上所述,就用指针吧。
例外
如果你的对象小到不需要以引用传递,你也许可以以值传递一个包裹在absl::optional
里的对象。例如:
void MyFunc(absl::optional<int> bar);
如果你期待你函数的所有的调用者都已经有一个对象包在absl::optional
里了,那你也许可以收一个const absl::optional&
。但是,这挺少见的;通常这只会发生在你的函数是文件/库的私有函数的时候。