应该如何使用std :: optional?

我正在阅读std::experimental::optional的文档,我对它做了什么有一个很好的想法,但我不明白什么时候应该使用它,或者我应该如何使用它。 该网站迄今尚未包含任何示例,这让我很难理解这个对象的真实概念。 什么时候std::optional是一个很好的select,它是如何弥补以前的标准(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 

这需要dynamic的内存分配,担心所有权等 – 总是喜欢上面的其他签名之一。


另一个例子:

 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 ”或者为“ max_count传递std::numeric_limits<double>::min() ”!


另一个例子:

 std::optional<int> find_in_string(std::string s, std::string query); 

如果查询string不在s ,我想要“不是int ” – 不是为了这个目的决定使用什么特殊的值(-1?)。


有关其他示例,可以查看boost::optional 文档 。 boost::optionalstd::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 } } } 

但是我不明白什么时候该使用它,或者我该如何使用它。

考虑一下,当你正在编写一个API,你想expression“没有返回”值不是一个错误。 例如,您需要从套接字中读取数据,并在数据块完成时parsing并返回:

 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 ::可选是一个很好的select

  • 当你计算一个值并且需要返回它的时候,通过值来返回更好的语义,而不是引用一个输出值(可能不会产生)。

  • 当你想确保客户端代码必须检查输出值(谁写的客户端代码可能不检查错误 – 如果你尝试使用未初始化的指针,你会得到一个核心转储;如果你试图使用un-初始化std ::可选,你得到一个可捕获的exception)。

它如何弥补以前的标准(C ++ 11)中没有的东西。

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

两者都要求客户实现者付出额外的努力和注意力,这两者都是造成混淆的原因(第一个推动客户端实现者将操作视为分配,并要求客户端代码实现指针处理逻辑,第二个允许客户端代码脱离使用无效/未初始化的值)。

std::optional很好地照顾了以前的解决scheme出现的问题。

我经常使用可选项来表示从configuration文件中提取的可选数据,也就是说可选地提供数据的位置(例如,在XML文档中的预期但不是必需的元素),以便我可以明确地清楚地显示数据实际上存在于XML文档中。 特别是当数据可以具有“未设定”状态时,相对于“空”和“设定”状态(模糊逻辑)。 使用可选项,设置和未设置是清楚的,也将清空值为0或null。

这可以显示“未设置”的值如何不等于“空”。 在概念上,指向int(int * p)的指针可以显示这一点,其中null(p == 0)未被设置,值为0(* p == 0)被设置并且为空,并且任何其他值(* p <> 0)被设置为一个值。

举一个实际的例子,我从一个叫做渲染标志的XML文档中取出了一个几何体,其中几何体可以覆盖渲染标志(set),禁用渲染标志(设置为0),或者根本不影响渲染标志(未设置),一个可选的将是一个明确的方式来表示这一点。

很明显,在这个例子中,指向int的指针可以实现目标,或者更好地实现共享指针,因为它可以提供更清晰的实现,但是,我认为这是关于代码清晰度的。 空始终是“未设置”? 有了一个指针,就不清楚了,因为null的字面意思是没有分配或创build,虽然它可能 ,但可能不一定意味着“未设置”。 值得指出的是,必须释放一个指针,然后在良好的实践中将其设置为0,但是像使用共享指针一样,可选项不需要显式清理,所以不需要将清理与可选没有设置。

我相信这是关于代码清晰度的。 清晰度降低了代码维护和开发的成本。 对代码意图的清楚理解是非常有价值的。

使用指针来表示这将需要重载指针的概念。 为了将“null”表示为“未设置”,通常可以通过代码看到一个或多个注释来解释这个意图。 这不是一个不错的解决scheme,而是一个可选的,但是,我总是select隐式的实现,而不是显式的注释,因为注释是不可强制执行的(如通过编译)。 这些用于开发的隐式项目(纯粹为强制意图而提供的那些项目)的例子包括各种C ++风格的转换,“const”(特别是在成员函数上)和“bool”types等等。 可以说,只要每个人都服从意图或评论,你并不需要这些代码function。