C++ 如何使用std::optional?
我正在阅读的文档,我对它的作用有一个很好的想法,但我不明白什么时候应该使用它或如何使用它。该网站还没有包含任何例子,这让我很难掌握这个对象的真正概念。什么时候使用C++ 如何使用std::optional?,c++,boost-optional,c++-tr2,C++,Boost Optional,C++ Tr2,我正在阅读的文档,我对它的作用有一个很好的想法,但我不明白什么时候应该使用它或如何使用它。该网站还没有包含任何例子,这让我很难掌握这个对象的真正概念。什么时候使用std::optional是一个好的选择,它如何补偿以前的标准(C++11)中没有的功能。我能想到的最简单的例子是: std::optional<int> try_parse_int(std::string s) { //try to parse an int from the given string, /
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
}
另一种方法是特别糟糕:
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;
};
template<typename Key, typename Value>
class Lookup
{
std::optional<Value> get(Key key);
};
std::vector<std::pair<std::string, double>> search(
std::string query,
std::optional<int> max_count,
std::optional<double> min_match_score);
std::optional<int> find_in_string(std::string s, std::string query);
如果查找中没有某个键,那么我们可以简单地返回“no value”
我可以这样使用它:
Lookup<std::string, std::string> location_lookup;
std::string location = location_lookup.get("waldo").value_or("unknown");
如果查询字符串不在s
中,我希望“noint
”--不要任何人决定用于此目的的特殊值(-1?)
有关其他示例,您可以查看
boost::optional
boost::optional
和std::optional
在行为和用法上基本相同。引用了以下示例:
可选str2int(字符串);//如果可能,将int转换为字符串
int从_用户()获取_int_
{
字符串s;
对于(;;){
cin>>s;
可选o=str2int;//“o”可以包含也可以不包含int
如果(o){//可选是否包含值?
return*o;//使用该值
}
}
}
但我不明白我什么时候应该使用它,或者我应该如何使用它
当您正在编写API并且希望表示“没有返回”值不是错误时,请考虑。例如,您需要从套接字读取数据,当数据块完成时,您可以解析它并返回它:
class YourBlock { /* block header, format, whatever else */ };
std::optional<YourBlock> cache_and_get_block(
some_socket_object& socket);
编辑:关于其余问题:
何时std::optional是一个好的选择
- 当您计算一个值并需要返回它时,按值返回比引用一个输出值(可能不会生成)更有利于语义
- 当您希望确保客户机代码必须检查输出值时(编写客户机代码的人可能不会检查错误-如果您尝试使用未初始化的指针,则会得到一个内核转储;如果您尝试使用未初始化的std::optional,则会得到一个可捕获的异常)
std::optional
很好地解决了以前解决方案中出现的问题。我经常使用optionals来表示从配置文件中提取的可选数据,也就是说,这些数据在哪里(例如,在XML文档中有一个预期但不必要的元素)是可选的,因此我可以显式清楚地显示数据是否实际存在于XML文档中。尤其是当数据可以具有“未设置”状态时,而不是“空”和“集”状态(模糊逻辑)。对于可选的,set和not set是清除的,也可以使用0或null值清除空的
这可以显示“not set”的值如何不等同于“empty”。在概念上,指向int(int*p)的指针可以显示这一点,其中未设置null(p==0),设置0(*p==0)的值并为空,并且将任何其他值(*p0)设置为值
对于一个实际示例,我从一个XML文档中提取了一段几何体,该文档具有一个名为render flags的值,其中的几何体可以覆盖render flags(设置)、禁用render flags(设置为0)或干脆不影响render flags(未设置),可选的方式可以清楚地表示这一点
很明显,在本例中,指向int的指针可以实现这一目标,或者更好的是,共享指针,因为它可以提供更清晰的实现,但是,我认为在这种情况下,它与代码的清晰度有关。null是否始终是“未设置”的值?对于指针,它是不清楚的,因为null字面上的意思是未分配或创建,尽管它可以,但可能不一定意味着“未设置”。值得指出的是,必须释放指针,并且在良好实践中设置为0,但是,与共享指针一样,可选指针不需要显式清理,因此不存在将清理与未设置的可选指针混淆的问题
我相信这是关于代码清晰性的。清晰性降低了代码维护和开发的成本。对代码意图的清晰理解是非常有价值的
使用指针来表示这一点需要重载指针的概念。要将“null”表示为“notset”,通常您可能会通过代码看到一个或多个注释来解释此意图。这是一个不错的解决方案,而不是可选的,但是,我始终选择隐式实现而不是显式注释,因为注释是不可执行的(例如通过编译)。这些隐式项的示例用于开发(开发中纯粹用于强制执行的文章)包括各种C++样式转换、“const”(尤其是成员函数)和“布尔”类型,仅举几个例子。
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
}
}
}
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 ]
}
}