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
中,我希望“no
int
”--不要任何人决定用于此目的的特殊值(-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,则会得到一个可捕获的异常)

[…]以及它如何补偿以前的标准(C++11)中没有的内容

在C++11之前,您必须为“可能不返回值的函数”使用不同的接口—通过指针返回并检查NULL,或者接受输出参数并为“不可用”返回错误/结果代码

这两种方法都需要客户机实现者付出额外的努力和关注才能使其正确,这两种方法都会引起混淆(第一种方法促使客户机实现者将操作视为分配,并要求客户机代码实现指针处理逻辑,第二种方法允许客户机代码使用无效/未初始化的值)


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 ]
    }
}