我正在阅读std::experimental::optional
的文档,对它的作用有所了解,但我不明白何时和如何使用它。该网站目前没有任何示例,这使我难以真正理解这个对象的概念。什么情况下使用std::optional
是一个好选择,并且它如何弥补在之前的标准中找不到的东西(C++11)。
我正在阅读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);
};
如果查询中没有特定的键,则可以简单地返回“没有值”。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
(或不需要),这种方式更加合理!
同时,它还能消除“诅咒”的问题,即“如果您不想设置限制,请为 max_count
传递 -1
”或“如果您不想设置最小分数,请为 min_match_score
传递 std::numeric_limits<double>::min()
”!
另一个例子:
std::optional<int> find_in_string(std::string s, std::string query);
如果查询字符串不在boost::optional
文档。 boost::optional
和 std::optional
在行为和用法方面基本相同。std::optional<T>
只是一个 T
和一个 bool
。成员函数的实现非常简单。在使用它时,性能不应该是一个问题 - 有时候某些东西是可选的,在这种情况下,这通常是正确的工具。 - Timothy Shieldsstd::optional<T>
比那复杂得多。它使用放置new和很多其他东西,具有适当的对齐和大小,以使其成为字面类型(即可与constexpr
一起使用),以及其他方面。天真的T
和bool
方法很快就会失败。 - Rapptzstorage_t
的联合体,其中包括一个未命名的unsigned char变量和一个名为value_
的T类型变量。第289行代码定义了一个结构体optional_base
,其中包括一个bool类型变量init_
、一个名为storage_
类型为storage_t<T>
的变量等。从概念上和具体实现来看,这种设计就是由一个T
类型的变量和一个bool类型的变量组成,但它的实现非常棘手和复杂。作者认为,如果只采用朴素的T
和bool
类型的方法,那么很快就会出现问题。你如何看待这段代码,才能得出这样一种结论呢? - Timothy Shieldsstruct{bool,maybe_t<T>}
,联合体只是避免了使用struct{bool,T}
,因为这样会在所有情况下都构造一个T。 - PeterTstd::unique_ptr<T>
和std::optional<T>
在某种程度上都扮演着“可选T”的角色。我认为它们之间的区别在于“实现细节”:额外的分配、内存管理、数据局部性、移动成本等等。例如,我永远不会写std::unique_ptr<int> try_parse_int(std::string s);
,因为这样每次调用都会导致一次分配,但实际上没有必要。我也永远不会写一个带有std::unique_ptr<double> limit;
的类——为什么要进行一次分配并且失去数据局部性呢? - Timothy Shields以下是从最新通过的文件: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
}
}
}
int
,而不是传递一些“幻影”值,假设其具有“错误”的含义。 - Luis Machucastr2int()
以任何希望的方式实现转换,(B) 不管获取 string s
的方法如何,(C) 通过 optional<int>
传达完整的含义,而不是一些愚蠢的魔法数字、bool
/引用或基于动态分配的方式来完成它。 - underscore_d但我不明白应该在什么情况下使用它,以及如何使用它。
考虑当您编写 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,要么接受输出参数并返回错误/结果代码表示“不可用”。
这两种方法都需要客户端实现者额外付出努力和注意力才能正确完成,并且都是混淆的源泉(第一种方法将客户端实现者推向将操作视为分配并需要客户端代码实现指针处理逻辑,第二种方法允许客户端代码使用无效/未初始化值)。
std::optional
很好地解决了之前解决方案中出现的问题。
boost::
而不是 std::
呢? - David Gboost::optional
是因为使用了大约两年后,它已经编码进了我的前额皮质)。 - utnapistim
std::optional
的情况下,模板参数不能是不完整类型(而对于std::unique_ptr
则可以)。更准确地说,标准要求_T[...]应满足可销毁的要求。 - dan_din_pantelimon