如何使用std::optional ?
How should one use std::optional?
我正在阅读std::experimental::optional
的文档,我对它的作用有一个很好的想法,但我不明白何时我应该使用它或如何使用它。这个网站还没有包含任何例子,这让我很难掌握这个对象的真正概念。什么时候使用std::optional
是一个好的选择,它如何弥补以前的标准(c++ 11)中没有找到的东西。
我能想到的最简单的例子:
std::optional<int> try_parse_int(std::string s)
{
//try to parse an int from the given string,
//and return "nothing" if you fail
}
同样的事情也可以用引用参数来完成(就像下面的签名一样),但是使用std::optional
会使签名和用法更好。
bool try_parse_int(std::string s, int& i);
另一种方法是尤其是:
int* try_parse_int(std::string s); //return nullptr if fail
这需要动态内存分配,担心所有权等-总是首选上述其他两个签名之一。
另一个例子:
class Contact
{
std::optional<std::string> home_phone;
std::optional<std::string> work_phone;
std::optional<std::string> mobile_phone;
};
这比为每个电话号码使用std::unique_ptr<std::string>
之类的东西更可取!std::optional
为您提供了数据局部性,这对性能很有好处。
另一个例子:
template<typename Key, typename Value>
class Lookup
{
std::optional<Value> get(Key key);
};
如果查找中没有某个键,则可以简单地返回"no value"。
我可以这样使用:
Lookup<std::string, std::string> location_lookup;
std::string location = location_lookup.get("waldo").value_or("unknown");
另一个例子:
std::vector<std::pair<std::string, double>> search(
std::string query,
std::optional<int> max_count,
std::optional<double> min_match_score);
这比四个函数重载更有意义,这些函数重载采用max_count
(或不)和min_match_score
(或不)的每种可能组合!
它也消除了诅咒"通过-1
的max_count
,如果你不想要一个限制"或"通过std::numeric_limits<double>::min()
的min_match_score
,如果你不想要一个最低分数"!
另一个例子:
std::optional<int> find_in_string(std::string s, std::string query);
如果查询字符串不在s
中,我想要"no int
"——not任何人决定为此目的使用的任何特殊值(-1?)
有关其他示例,您可以查看boost::optional
文档。boost::optional
和std::optional
在行为和使用方面基本相同。
引用New adopt paper: N3672, std::optional:
optional<int> str2int(string); // converts int to string if possible
int get_int_from_user()
{
string s;
for (;;) {
cin >> s;
optional<int> o = str2int(s); // 'o' may or may not contain an int
if (o) { // does optional contain a value?
return *o; // use the value
}
}
}
但我不明白什么时候该用,怎么用
考虑当你在编写API时,你想表达"没有返回"值不是错误。例如,您需要从套接字读取数据,当数据块完成时,您解析它并返回它:
class YourBlock { /* block header, format, whatever else */ };
std::optional<YourBlock> cache_and_get_block(
some_socket_object& socket);
如果附加的数据完成了一个可解析块,你可以处理它;否则,继续读取和追加数据:
void your_client_code(some_socket_object& socket)
{
char raw_data[1024]; // max 1024 bytes of raw data (for example)
while(socket.read(raw_data, 1024))
{
if(auto block = cache_and_get_block(raw_data))
{
// process *block here
// then return or break
}
// else [ no error; just keep reading and appending ]
}
}
编辑:关于你剩下的问题:
当std::optional是使用
的好选择时
当您计算一个值并需要返回它时,按值返回比引用输出值(可能不会生成)具有更好的语义。
当你想确保客户端代码有检查输出值(写客户端代码的人可能不会检查错误-如果你试图使用一个未初始化的指针,你会得到一个核心转储;如果你试图使用一个未初始化的std::optional,你会得到一个可捕获的异常)。
[…]以及它如何补偿以前的标准(c++ 11)中没有找到的内容。
在c++ 11之前,你必须为"可能不返回值的函数"使用不同的接口——要么通过指针返回并检查是否为NULL,要么接受输出参数并返回错误/结果代码"not available"。
两者都需要客户端实现者付出额外的努力和注意力才能正确完成,两者都是混乱的根源(第一个要求客户端实现者将操作视为分配,并要求客户端代码实现指针处理逻辑,第二个允许客户端代码使用无效/未初始化的值)。
std::optional
很好地处理了先前解决方案中出现的问题。
我经常使用可选选项来表示从配置文件中提取的可选数据,也就是说,可选地提供该数据(例如XML文档中预期的但不是必需的元素)的位置,以便我可以显式和清晰地显示数据是否实际存在于XML文档中。特别是当数据可以具有"未设置"状态,而不是"空"answers"设置"状态(模糊逻辑)时。对于可选选项,set和not set是明确的,同样empty将被明确为值0或null。
这可以显示"not set"的值如何不等同于"empty"。从概念上讲,指向int (int *p)的指针可以显示这一点,其中未设置null (p == 0),设置0值(*p == 0)并为空,并将任何其他值(*p <> 0)设置为值。
对于一个实际的例子,我有一个从XML文档中提取的几何体,它的值称为呈现标志,其中几何体可以覆盖呈现标志(设置),禁用呈现标志(设置为0),或者简单地不影响呈现标志(未设置),可选将是一个清晰的方式来表示这一点。
很明显,在这个例子中,指向int的指针可以达到这个目标,或者更好的是,共享指针可以提供更清晰的实现,但是,我认为在这个例子中,它是关于代码清晰度的。null总是"未设置"吗?对于指针,这是不清楚的,因为null字面上表示未分配或未创建,尽管它可以,但不一定表示"未设置"。值得指出的是,指针必须被释放,并且在良好的实践中必须设置为0,然而,与共享指针一样,可选指针不需要显式清除,因此不需要担心将清除与未设置的可选指针混淆。
我相信这是关于代码的清晰度。清晰度降低了代码维护和开发的成本。对代码意图的清晰理解是非常有价值的。
使用指针来表示这将需要重载指针的概念。要将"null"表示为"not set",通常您可能会在代码中看到一条或多条注释来解释这一意图。这是一个不错的解决方案,而不是一个可选的解决方案,然而,我总是选择隐式实现而不是显式注释,因为注释是不可强制执行的(例如通过编译)。这些用于开发的隐式项的示例(那些在开发中纯粹为了强制执行意图而提供的条目)包括各种c++风格强制转换,"const"(特别是在成员函数上)和"bool"类型,仅举几例。可以说,只要每个人都遵守意图或注释,您就不需要这些代码特性。
- 为什么 std::optional::operator=(U&&) 要求你是非标量类型?
- 检查某些类型是否是模板类 std::optional 的实例化
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- 使用 std::optional,而不是自己的结构
- C++17:使用 std::optional 来评估枚举是否包含值
- 从返回 std::optional of std::vector 的函数中获取结果到调用方
- API 返回智能指针的 std::optional 以明确指定指针可能为 null 是否有意义?
- 函数可以应用于 std::optional,并返回一个可选值吗?
- 为什么 std::optional::value_or 没有默认 ctor 类型的专用化?
- 使用std::optional时的命名返回值优化
- std::function<std::optional<T>()> 如何与返回 T 的 lambda 兼容?
- 如何正确地将 boost::optional<std::chrono::d uration> 作为函数参数?
- 如何从 std::optional 中获取 QByteArray<QByteArray>,并在没有其他 malloc 的情况下保留 std::nullopt?
- 为什么返回std::optional有时移动,有时复制
- 为什么 std::optional 的强制转换运算符被忽略了
- 使用 std::experimental::optional,给出编译错误
- C++是否在 std::min 和 std::max 下标准化 std::optional 的行为?
- 返回类型 std::optional<std::variant<...>>
- std::optional<std::reference_wrapper<T>> - 可以吗?
- boost::optional<std::string> 和来自 char[] 的隐式构造函数