0x4*_*2D2 120 c++ boost-optional c++-tr2
我正在阅读文档,std::experimental::optional
我对它的作用有很好的了解,但我不明白何时应该使用它或我应该如何使用它.该网站不包含任何例子,这使我更难理解这个对象的真实概念.何时是std::optional
一个很好的选择,它如何补偿以前的标准(C++ 11)中没有找到的东西.
Tim*_*lds 167
我能想到的最简单的例子:
std::optional<int> try_parse_int(std::string s)
{
//try to parse an int from the given string,
//and return "nothing" if you fail
}
Run Code Online (Sandbox Code Playgroud)
使用引用参数可以完成相同的操作(如下面的签名),但使用std::optional
使签名和使用更好.
bool try_parse_int(std::string s, int& i);
Run Code Online (Sandbox Code Playgroud)
另一种可以做到这一点的方法尤其糟糕:
int* try_parse_int(std::string s); //return nullptr if fail
Run Code Online (Sandbox Code Playgroud)
这需要动态内存分配,担心所有权等等 - 总是更喜欢上面其他两个签名中的一个.
另一个例子:
class Contact
{
std::optional<std::string> home_phone;
std::optional<std::string> work_phone;
std::optional<std::string> mobile_phone;
};
Run Code Online (Sandbox Code Playgroud)
std::unique_ptr<std::string>
对于每个电话号码而言,这是非常可取的!std::optional
为您提供数据位置,这对性能非常有用.
另一个例子:
template<typename Key, typename Value>
class Lookup
{
std::optional<Value> get(Key key);
};
Run Code Online (Sandbox Code Playgroud)
如果查找中没有某个键,那么我们可以简单地返回"无值".
我可以像这样使用它:
Lookup<std::string, std::string> location_lookup;
std::string location = location_lookup.get("waldo").value_or("unknown");
Run Code Online (Sandbox Code Playgroud)
另一个例子:
std::vector<std::pair<std::string, double>> search(
std::string query,
std::optional<int> max_count,
std::optional<double> min_match_score);
Run Code Online (Sandbox Code Playgroud)
这比使用四个函数重载更有意义,它们采用max_count
(或不)和min_match_score
(或不)的每种可能组合!
它也消除了诅咒 "通过-1
了max_count
,如果你不想限制"或"合格std::numeric_limits<double>::min()
的min_match_score
,如果你不想要一个最低分"!
另一个例子:
std::optional<int> find_in_string(std::string s, std::string query);
Run Code Online (Sandbox Code Playgroud)
如果查询字符串不在s
,我想要"不int
" - 不是某人为此目的决定使用的特殊值(-1?).
有关其他示例,您可以查看boost::optional
文档.boost::optional
并且std::optional
在行为和使用方面基本相同.
tao*_*ocp 32
一个例子引自新收养的论文: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
}
}
}
Run Code Online (Sandbox Code Playgroud)
但我不明白何时应该使用它或我应该如何使用它.
考虑何时编写API并且您想表达"没有返回"值不是错误.例如,您需要从套接字读取数据,并在数据块完成时解析它并返回它:
class YourBlock { /* block header, format, whatever else */ };
std::optional<YourBlock> cache_and_get_block(
some_socket_object& socket);
Run Code Online (Sandbox Code Playgroud)
如果附加数据完成了可解析块,则可以对其进行处理; 否则,继续阅读和追加数据:
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 ]
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:关于其他问题:
什么时候std :: optional是一个很好的选择
当您计算一个值并需要返回它时,它会使值更好地返回语义,而不是引用输出值(可能不会生成).
当你想确保客户端代码必须检查输出值时(编写客户端代码的人可能不会检查错误 - 如果你试图使用未初始化的指针,你会得到核心转储;如果你试图使用un-初始化std :: optional,你得到一个可以捕获的异常).
[...]以及它如何弥补之前标准(C++ 11)中没有的内容.
在C++ 11之前,您必须为"可能不返回值的函数"使用不同的接口 - 通过指针返回并检查NULL,或接受输出参数并返回"不可用"的错误/结果代码".
两者都给予客户端实现者额外的努力和关注以使其正确并且两者都是混淆的源头(第一个推动客户端实现者将操作视为分配并要求客户端代码实现指针处理逻辑而第二个允许使用无效/未初始化值的客户端代码).
std::optional
很好地处理以前解决方案中出现的问题.
我经常使用可选项来表示从配置文件中提取的可选数据,也就是说,该数据(例如 XML 文档中预期的但不是必需的元素)是可选提供的,以便我可以明确并清楚地显示数据实际上存在于 XML 文档中。特别是当数据可以具有“未设置”状态,而不是“空”和“设置”状态(模糊逻辑)时。对于可选,set 和 not set 是明确的,也可以通过值为 0 或 null 来清除空。
这可以说明“未设置”的值如何不等同于“空”。在概念上,指向 int (int * p) 的指针可以显示这一点,其中未设置 null (p == 0),设置值为 0 (*p == 0) 且为空,以及任何其他值(*p <> 0) 设置为一个值。
举一个实际的例子,我从一个 XML 文档中提取了一个几何图形,它有一个名为渲染标志的值,其中几何图形可以覆盖渲染标志(设置),禁用渲染标志(设置为 0),或者根本不影响渲染标志(未设置),可选将是表示这一点的明确方式。
显然,在本例中,指向 int 的指针可以实现目标,或者更好的是,使用共享指针,因为它可以提供更清晰的实现,但是,在这种情况下,我认为这与代码清晰度有关。空值总是“未设置”吗?对于指针,尚不清楚,因为 null 字面意思是未分配或未创建,尽管它可以,但不一定意味着“未设置”。值得指出的是,必须释放一个指针,并且在良好的实践中设置为 0,但是,与共享指针一样,可选不需要显式清理,因此不必担心将清理与可选的尚未设置。
我相信这是关于代码清晰度的。Clarity 降低了代码维护和开发的成本。对代码意图的清晰理解是非常有价值的。
使用指针来表示这将需要重载指针的概念。要将“null”表示为“not set”,通常您可能会通过代码看到一个或多个注释来解释这一意图。这不是一个糟糕的解决方案,而不是一个可选的解决方案,但是,我总是选择隐式实现而不是显式注释,因为注释是不可强制执行的(例如通过编译)。这些用于开发的隐式项的示例(开发中提供的那些纯粹是为了强制执行意图的文章)包括各种 C++ 样式强制转换、“const”(特别是在成员函数上)和“bool”类型,仅举几例。可以说,您并不真正需要这些代码功能,只要每个人都遵守意图或意见即可。
归档时间: |
|
查看次数: |
69293 次 |
最近记录: |