result<>
我们将定义一个将 std::string
转换为 int
的函数。这个函数可能会因为多种原因而失败;如果失败了,我们希望传达失败的原因。
outcome::result<int> convert(const std::string& str) noexcept;
模板别名 result<T, E = varies, NoValuePolicy = policy::default_policy<T, E, void>>
有三个模板参数,但最后两个有默认值。第一个 (T
) 表示函数成功时返回的对象的类型。第二个 (EC
) 是包含函数失败时失败原因信息的对象的类型。result 对象在任何给定时刻存储 T
或 EC
,因此在概念上类似于 variant<T, EC>
。在独立的 Outcome 中,EC
默认为 std::error_code
,在 Boost.Outcome 中,EC
默认为 boost::system::error_code
1。第三个参数 (NoValuePolicy
) 称为无值策略。我们将在后面介绍它。
如果 T
和 EC
都是 可平凡复制的,则 result<T, EC, NVP>
也是可平凡复制的。每个操作(构造、复制构造、赋值、析构等)的平凡性、复杂性和 constexpr 性质始终是 T
和 EC
中相应操作的交集,因此例如,如果 T
和 EC
都是 字面类型,则 result<T, EC, NVP>
也将是字面类型。此外,如果 T
和 EC
都具有 标准布局,则 result<T, EC, NVP>
具有标准布局;因此,如果 T
和 EC
都是 C 兼容的,则 result<T, EC, NVP>
也将是 C 兼容的。
现在,我们将定义一个枚举,描述转换期间的不同失败情况。
enum class ConversionErrc
{
Success = 0, // 0 should not represent an error
EmptyString = 1, // (for rationale, see tutorial on error codes)
IllegalChar = 2,
TooLong = 3,
};
// all boilerplate necessary to plug ConversionErrc
// into std::error_code framework
假设我们已将其插入到 std::error_code
框架中,如 本节 中所述。
这种插入的一个显著效果是 ConversionErrc
现在可以转换为 std::error_code
。现在我们可以按如下方式实现函数 convert
outcome::result<int> convert(const std::string& str) noexcept
{
if (str.empty())
return ConversionErrc::EmptyString;
if (!std::all_of(str.begin(), str.end(), ::isdigit))
return ConversionErrc::IllegalChar;
if (str.length() > 9)
return ConversionErrc::TooLong;
return atoi(str.c_str());
}
result<T, EC>
可以从任何可转换为 T
的 T2
以及任何可转换为 EC
的 EC2
转换而来,前提是 T
和 EC
之间在任一方向上都不可能进行构造。如果存在,则所有隐式转换都将被禁用,您将需要使用标记构造函数之一
outcome::result<int> r {outcome::in_place_type<std::error_code>, ConversionErrc::EmptyString};
outcome::result<int> s {outcome::in_place_type<int>, 1};
或使用辅助函数
outcome::result<int> r = outcome::failure(ConversionErrc::EmptyString);
outcome::result<int> s = outcome::success(1);
- 您可以使用
std_result<T>
或boost_result<T>
来强制选择。[返回]