随着 C++ 获得了可选值(在 C++17 中),现在通常需要编写以下内容:
如果条件成立,则用某个表达式初始化我的变量;如果条件不成立,请将我的变量保留为空/未分配
我们如何用 C++ 编写它?假设我们的表达式是
int
类型。人们希望可行的“理想”或最方便的语法是:
auto myopt = some_condition ? std::nullopt : 123;
但可选的是库类型,该语言对此一无所知,因此没有太多理由将
nullopt_t
与 int
协调为 optional<int>
。
所以,让我们尝试更详细一点:
std::optional<int> myopt = some_condition ? std::nullopt : 123;
这仍然失败,因为
int
无法转换为nullopt_t
,反之亦然。
我们可以这样写:
auto myopt = some_condition ? std::nullopt : std::optional<int>(123);
这还可以。但如果我们的类型不是普通整数而是更复杂的东西,我们又会遇到麻烦:
struct mystruct { int x; double y; };
/// -snip-
auto myopt = some_condition ? std::nullopt : std::optional<mystruct>{ 12, 3.4 };
...无法编译。
所以,
使用值或 nullopt 初始化可选值的体面、简洁(DRY...)习惯用法是什么?请注意,它也必须适用于更复杂的类型。
这里有三种选择,从我最不喜欢的到我最喜欢的:
in_place_t
std::optional
在您的问题中,您尝试使用
optional<T>
或 nullopt_t
pr-value来初始化
T
。但是 - 在可选选项中就地构建 T
怎么样?事实上,通过明确地说你想这样做,这是可能的,尽管不是很漂亮。 optional<T>
有构造函数:
template< class... Args >
constexpr explicit optional( std::in_place_t, Args&&... args );
在你的情况下,它的使用意味着写作:
auto myopt = some_condition ? std::nullopt : std::optional<mystruct>{ std::in_place_t{}, 12, 3.4 };
编译并执行您想要的操作。可以观察到干燥,但它并不漂亮。
让我们尝试对代码进行最小更改的方法。
幸运的是,在 C++17 中我们不仅仅获得了可选的,我们还获得了类模板参数推导(CTAD)指南,特别是针对可选的。所以,这有效:
auto myopt = some_condition ? std::nullopt : std::optional{ mystruct{ 12, 3.4 } };
并且您刚刚替换了
optional<T>{ ... }
{with
可选{ T{ ... } }`。还不错。
如果你希望初始化更简洁(同时仍然具有合理的可读性),你可以写:
template <typename T>
auto value_if(T&& t, bool cond) {
return cond ? std::nullopt : std::optional{ std::forward<T>(t) };
}
然后像这样使用它:
auto myopt = value_if(mystruct{12, 3.4}, some_condition);
这是我目前最喜欢的方法:-)