C++ 应该如何使用 std::optional ?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16860960/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How should one use std::optional?
提问by 0x499602D2
I'm reading the documentation of std::experimental::optional
and I have a good idea about what it does, but I don't understand whenI should use it or how I should use it. The site doesn't contain any examples as of yet which leaves it harder for me to grasp the true concept of this object. When is std::optional
a good choice to use, and how does it compensate for what was not found in the previous Standard (C++11).
我正在阅读 的文档std::experimental::optional
并且我对它的作用有一个很好的了解,但我不明白什么时候应该使用它或我应该如何使用它。该站点尚未包含任何示例,这让我更难掌握该对象的真实概念。什么时候使用是std::optional
一个不错的选择,它如何弥补以前标准 (C++11) 中没有的内容。
回答by Timothy Shields
The simplest example I can think of:
我能想到的最简单的例子:
std::optional<int> try_parse_int(std::string s)
{
//try to parse an int from the given string,
//and return "nothing" if you fail
}
The same thing might be accomplished with a reference argument instead (as in the following signature), but using std::optional
makes the signature and usage nicer.
同样的事情可以用一个引用参数来完成(如下面的签名),但使用std::optional
会使签名和用法更好。
bool try_parse_int(std::string s, int& i);
Another way that this could be done is especially bad:
另一种可以做到这一点的方法特别糟糕:
int* try_parse_int(std::string s); //return nullptr if fail
This requires dynamic memory allocation, worrying about ownership, etc. - always prefer one of the other two signatures above.
这需要动态内存分配,担心所有权等 - 总是喜欢上面其他两个签名之一。
Another example:
另一个例子:
class Contact
{
std::optional<std::string> home_phone;
std::optional<std::string> work_phone;
std::optional<std::string> mobile_phone;
};
This is extremely preferable to instead having something like a std::unique_ptr<std::string>
for each phone number! std::optional
gives you data locality, which is great for performance.
这是非常可取的,而不是std::unique_ptr<std::string>
为每个电话号码设置类似的东西!std::optional
为您提供数据局部性,这对性能非常有用。
Another example:
另一个例子:
template<typename Key, typename Value>
class Lookup
{
std::optional<Value> get(Key key);
};
If the lookup doesn't have a certain key in it, then we can simply return "no value."
如果查找中没有某个键,那么我们可以简单地返回“无值”。
I can use it like this:
我可以这样使用它:
Lookup<std::string, std::string> location_lookup;
std::string location = location_lookup.get("waldo").value_or("unknown");
Another example:
另一个例子:
std::vector<std::pair<std::string, double>> search(
std::string query,
std::optional<int> max_count,
std::optional<double> min_match_score);
This makes a lot more sense than, say, having four function overloads that take every possible combination of max_count
(or not) and min_match_score
(or not)!
这比使用max_count
(或不)和min_match_score
(或不)的每种可能组合的四个函数重载更有意义!
It also eliminatesthe accursed"Pass -1
for max_count
if you don't want a limit" or "Pass std::numeric_limits<double>::min()
for min_match_score
if you don't want a minimum score"!
它也消除了诅咒“通过-1
了max_count
,如果你不想限制”或“通行证std::numeric_limits<double>::min()
的min_match_score
,如果你不想要一个最低分”!
Another example:
另一个例子:
std::optional<int> find_in_string(std::string s, std::string query);
If the query string isn't in s
, I want "no int
" -- notwhatever special value someone decided to use for this purpose (-1?).
如果查询字符串不在 中s
,我想要“no int
”——不是某人决定为此目的使用的任何特殊值(-1?)。
For additional examples, you could look at the boost::optional
documentation. boost::optional
and std::optional
will basically be identical in terms of behavior and usage.
有关其他示例,您可以查看boost::optional
文档。boost::optional
并且std::optional
在行为和用法方面基本相同。
回答by taocp
An example is quoted from New adopted paper: N3672, std::optional:
从新采用的论文中引用了一个例子: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
}
}
}
回答by utnapistim
but I don't understand when I should use it or how I should use it.
但我不明白我什么时候应该使用它或我应该如何使用它。
Consider when you are writing an API and you want to express that "not having a return" value is not an error. For example, you need to read data from a socket, and when a data block is complete, you parse it and return it:
考虑当您编写 API 并且您想表达“没有返回”值不是错误时。比如你需要从一个socket中读取数据,当一个数据块完成时,你解析它并返回它:
class YourBlock { /* block header, format, whatever else */ };
std::optional<YourBlock> cache_and_get_block(
some_socket_object& socket);
If the appended data completed a parsable block, you can process it; otherwise, keep reading and appending data:
如果附加的数据完成了一个可解析的块,则可以对其进行处理;否则,继续读取和附加数据:
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 ]
}
}
Edit: regarding the rest of your questions:
编辑:关于您的其余问题:
When is std::optional a good choice to use
什么时候 std::optional 是一个不错的选择
When you compute a value and need to return it, it makes for better semantics to return by value than to take a reference to an output value (that may not be generated).
When you want to ensure that client code hasto check the output value (whoever writes the client code may not check for error - if you attempt to use an un-initialized pointer you get a core dump; if you attempt to use an un-initialized std::optional, you get a catch-able exception).
当您计算一个值并需要返回它时,按值返回比引用输出值(可能不会生成)更好的语义。
当您想确保客户端代码必须检查输出值时(编写客户端代码的人可能不会检查错误 - 如果您尝试使用未初始化的指针,您将获得核心转储;如果您尝试使用未初始化的指针)初始化 std::optional,你会得到一个可捕获的异常)。
[...] and how does it compensate for what was not found in the previous Standard (C++11).
[...] 以及它如何补偿之前标准 (C++11) 中未发现的内容。
Previous to C++11, you had to use a different interface for "functions that may not return a value" - either return by pointer and check for NULL, or accept an output parameter and return an error/result code for "not available".
在 C++11 之前,您必须对“可能不返回值的函数”使用不同的接口——要么通过指针返回并检查 NULL,要么接受输出参数并返回“不可用”的错误/结果代码”。
Both impose extra effort and attention from the client implementer to get it right and both are a source of confusion (the first pushing the client implementer to think of an operation as an allocation and requiring client code to implement pointer-handling logic and the second allowing client code to get away with using invalid/uninitialized values).
两者都对客户端实现者施加额外的努力和关注以使其正确,两者都是混淆的根源(第一个推动客户端实现者将操作视为分配并要求客户端代码实现指针处理逻辑,第二个允许客户端代码以逃避使用无效/未初始化的值)。
std::optional
nicely takes care of the problems arising with previous solutions.
std::optional
很好地解决了以前的解决方案出现的问题。
回答by Kit10
I often use optionals to represent optional data pulled from configuration files, that is to say where that data (such as with an expected, yet not necessary, element within an XML document) is optionally provided, so that I can explicitly and clearly show if the data was actually present in the XML document. Especially when the data can have a "not set" state, versus an "empty" and a "set" state (fuzzy logic). With an optional, set and not set is clear, also empty would be clear with the value of 0 or null.
我经常使用可选项来表示从配置文件中提取的可选数据,也就是说,该数据(例如 XML 文档中预期的但不是必需的元素)是可选提供的,以便我可以明确并清楚地显示数据实际上存在于 XML 文档中。特别是当数据可以具有“未设置”状态,而不是“空”和“设置”状态(模糊逻辑)时。对于可选,set 和 not set 是明确的,也可以通过值为 0 或 null 来清除空。
This can show how the value of "not set" is not equivalent to "empty". In concept, a pointer to an int (int * p) can show this, where a null (p == 0) is not set, a value of 0 (*p == 0) is set and empty, and any other value (*p <> 0) is set to a value.
这可以说明“未设置”的值如何不等同于“空”。在概念上,指向 int (int * p) 的指针可以显示这一点,其中未设置 null (p == 0),设置值为 0 (*p == 0) 且为空,以及任何其他值(*p <> 0) 设置为一个值。
For a practical example, I have a piece of geometry pulled from an XML document that had a value called render flags, where the geometry can either override the render flags (set), disable the render flags (set to 0), or simply not affect the render flags (not set), an optional would be a clear way to represent this.
举一个实际的例子,我从一个 XML 文档中提取了一个几何图形,它有一个名为渲染标志的值,其中几何图形可以覆盖渲染标志(设置),禁用渲染标志(设置为 0),或者根本不影响渲染标志(未设置),可选将是表示这一点的明确方式。
Clearly a pointer to an int, in this example, can accomplish the goal, or better, a share pointer as it can offer cleaner implementation, however, I would argue it's about code clarity in this case. Is a null always a "not set"? With a pointer, it is not clear, as null literally means not allocated or created, though it could, yet might not necessarilymean "not set". It is worth pointing out that a pointer must be released, and in good practice set to 0, however, like with a shared pointer, an optional doesn't require explicit cleanup, so there isn't a concern of mixing up the cleanup with the optional having not been set.
显然,在本例中,指向 int 的指针可以实现目标,或者更好的是,使用共享指针,因为它可以提供更清晰的实现,但是,在这种情况下,我认为这与代码清晰度有关。空值总是“未设置”吗?对于指针,尚不清楚,因为 null 字面意思是未分配或未创建,尽管它可以,但不一定意味着“未设置”。值得指出的是,必须释放一个指针,并且在良好的实践中设置为 0,但是,与共享指针一样,可选不需要显式清理,因此不必担心将清理与可选的尚未设置。
I believe it's about code clarity. Clarity reduces the cost of code maintenance, and development. A clear understanding of code intention is incredibly valuable.
我相信这是关于代码清晰度的。Clarity 降低了代码维护和开发的成本。对代码意图的清晰理解是非常有价值的。
Use of a pointer to represent this would require overloading the concept of the pointer. To represent "null" as "not set", typically you might see one or more comments through code to explain this intention. That's not a bad solution instead of an optional, however, I always opt for implicit implementation rather than explicit comments, as comments are not enforceable (such as by compilation). Examples of these implicit items for development (those articles in development that are provided purely to enforce intention) include the various C++ style casts, "const" (especially on member functions), and the "bool" type, to name a few. Arguably you don't really need these code features, so long as everyone obeys intentions or comments.
使用指针来表示这将需要重载指针的概念。要将“null”表示为“not set”,通常您可能会通过代码看到一个或多个注释来解释这一意图。这不是一个糟糕的解决方案,而不是一个可选的解决方案,但是,我总是选择隐式实现而不是显式注释,因为注释是不可强制执行的(例如通过编译)。这些用于开发的隐式项的示例(开发中提供的那些纯粹是为了强制执行意图的文章)包括各种 C++ 样式强制转换、“const”(特别是在成员函数上)和“bool”类型,仅举几例。可以说,您并不真正需要这些代码功能,只要每个人都遵守意图或意见即可。