当我开始利用 C++17 结构化绑定和 if 运算符 init 语句来进行更优雅的函数结果报告和检查时,如果符合 C++ 核心指南 F21,我开始执行以下操作:
std::pair<bool, int>Foo()
{
return {true, 42}; //true means that function complete with no error and that 42 is a good value
}
void main(void)
{
if (auto [Result, Value] = Foo(); Result)
{
//Do something with the return value here
}
}
然后,当然,我认为对于此类返回类型有一个可重用的模板会很好,这样就没有人需要复制该对的 bool 部分:
template <typename T> using validated = std::pair<bool,T>;
validated<int> Foo()
{
return {true, 42};
}
void main(void)
{
if (auto [Result, Value] = Foo(); Result)
{
//Do something with the return value here
}
}
这对我来说非常有用,但现在我想知道是否有某种与此模板等效的标准,这样我就不必重新发明轮子并自己定义它。似乎任意类型值加上有效性标志将是一个有用的构造,但我在标准库中找不到任何内容。我是不是错过了什么?
std::可选正是您要问的。甚至在描述中:
可选的一个常见用例是可能失败的函数的返回值。与其他方法(例如
)相反,可选可以很好地处理构建成本高昂的对象,并且更具可读性,因为意图是明确表达的。std::pair<T,bool>
示例中的
if
看起来更简单一些:
#include <optional>
#include <iostream>
std::optional<int> Foo(bool fail)
{
if (!fail) return {42};
return {};
}
void process(bool fail) {
if (auto val = Foo(fail)) {
std::cout << val.value() << '\n';
} else {
std::cout << "No value!\n";
}
}
int main() {
std::optional<int> oi;
process(true);
process(false);
}
如果您确实希望明确使用
Value
,那么您始终可以通过成功分支上的引用来解压它,即 auto Value = val.value()
;
您需要注意一些注意事项。 2 从我的头顶:
注意:为简洁起见,为
添加了static
- 以防止生成外部链接版本。process
false
。这可能会让一些人感到惊讶,optional
的默认构造并不默认构造底层值。编辑: 在评论之后,我决定明确声明 不存在类似
pair<T,bool>
的类型别名或与标准库兼容的类似内容。证明某些东西不存在并不容易,但如果存在这样的类型,标准库肯定会在 insert
的声明中使用它,但事实并非如此;因此,我强烈暗示它没有任何语义包装器。
std::expected
(在 C++23 中引入)感兴趣。
它的界面与
std::optional
非常接近。主要优点
expected<T, E>
超过 optional<T>
是传输错误的能力:
enum class errc {err1, err2, err3};
std::expected<int, errc> Foo()
{
if (/* error condition 1 */) return std::unexpected(errc::err1);
// ... checking other error conditions
return 42; // no error condition (42 is a good value)
// implicit conversion from `int` to `expected<int, errc>`
// avoid boilerplate code
}
int main()
{
auto q = Foo();
if (q)
{
// Do something with the return value here
}
}
您还可以看看:
main()
必须返回int
。