std::optional
如何适合我的代码?我的代码包含许多看起来像这样的函数:
bool GetName(_Out_ string& strRes)
{
// try to get/calculate the value
// value may also be empty
// return true on success, false otherwise.
}
现在我找到了std::optional,我只需编写代码:
std::optional<std::string> GetName()
std::optional
?std::optional
会带来性能价格,并且为了争辩,让我们假设我愿意支付。std::optional<int> iVal = 9;
毫无意义。std::optional
迷住了吗?我发现std::optional
很自然,以至于我得出的结论是,默认情况下是我的许多本机类型,bool
,int
,{ {1}},应声明为string
。
因此,与其遵循以下前提:
仅在绝对需要时使用
std::optional
我倾向于:
始终{strong>除非使用
std::optional
,否则您确定现在不需要了,或者 将来。
以下规则是否有效:
在以下情况下使用std::optional
如果使用std::optional
变得很丰富,我是否有冒着代码可读性问题的风险?
答案和评论使讨论偏离了我最初的关注,这是试图为何时使用std::optional
定义经验法则。
我将介绍一些常见的反馈意见
您正在编码C样式的错误检查功能。
计算失败时使用异常。
提供一个不太好的我的动机例子也许是我的错。 std::optional
仅是我常用的案例。
我不想关注错误的反映方式(如果有的话),而是关注是否为可选项命名了一个好候选人?
我没有声明如果GetName()
返回false,则暗示我的流程中存在某种错误。当GetName
返回false时,我可以生成一个随机名称,也可以忽略当前迭代。无论调用者如何对返回值做出反应,GetName
在name
的意义上都是optional
。
我可以提供一个更好的示例:
may not be present
在此函数中,由于我想区分两个容器,因此我被“强制”提供了两个容器:
void GetValuesFromDB(vector<string> vecKeys, map<string,string> mapKeyValue);
mapKeyValue
但是有了mapKeyValue
我可以去:
optional
我相信我们也从字面上理解void GetValuesFromDB(map<string,std::optional<string> > mapKeyValue);
一词。
我真的支持有人可以使用optional
If you want to represent a nullable type nicely. Rather than using unique values (like -1, nullptr, NO_VALUE or something)
c ++标准委员会可以轻松决定将std::optional
命名为std::optional
。
答案 0 :(得分:3)
我在滥用std :: optional
也许。
std::optional
的预期语义是指示某些东西是可选的。也就是说,如果事物不存在,那是可以的,不是错误。
如果您的方法失败,它将返回一个空的可选内容以指示失败。但是此失败是否也是错误取决于调用代码所期望的语义,您没有显示或描述该语义:如果可选结果如何处理?是空的吗?
例如,
auto name = GetName();
if (!name) { diediedie(FATAL_ERROR); }
建议该名称并不是真正的可选名称。如果您不能(如您所说)在此处使用异常,则variant<string, error>
方法看起来更清晰,并且还可以让失败的函数传达失败原因,否则将导致失败。
相反
auto name = GetName();
if (name) {
cout << "User is called " << *name << '\n';
} else {
cout << "User is anonymous\n";
}
似乎合理,因为未能获取名称不是错误。
为什么... 不使用
std::optional
?
当您不想传达某些信息是可选时。
显然,您可以滥用它,就像其他任何事物一样,但是它可能会造成混乱,误导,意外和其他不良习惯。
我是不是太迷住了……?
也许。如果您的错误处理污染了您的所有代码,则您可能应该考虑更早失败,而不是继续尝试那些永远无法成功的逻辑。
如果您确实有很多可能合法丢失的变量(就程序逻辑而言),那么您可能正确使用了它。
问题1
同样,这取决于程序上下文中故障的含义。
问题2
是的,如果 everything 是可选的,那么您的代码将是一团乱码,部分是由于视觉噪音,但主要是因为您无法确定逻辑中的哪个点甚至存在变量
您的GetValuesFromDB
编辑可能可能可以使用可选的 if ,因为您的数据库相对来说是非结构化的,没有任何约束。您不能依赖于正在填充的任何特定列,所有列都是可选的,您需要手动检查每个字段的存在。它们是真正可选的。
如果您的数据库具有高度的结构性,则对主键使用可选可能是 。您不能在没有主键的情况下没有行,因此使其成为可选键似乎有些奇怪。不过,也许不值得为必不可少的几个列提供一个单独的接口。
我相信我们在字面上也可以使用“可选”一词。
我真的无法告诉您您是否读过我的答案-您的评论至少表明您不理解。
假设我们将其称为可为空。我会仍然建议,对于可能合法不存在的事物返回nullable<T>
是好的,但是对于缺少指示错误的事物返回相同的类型是错误的。
您,您必须确定什么构成代码错误。我们不能为您做到这一点,尤其是提供的信息。那么,您应该对所有内容使用可选吗?也许。这就是我说的。确定您自己的代码的语义和前提条件,您将知道哪些是可选的,哪些是强制性的,哪些不存在应作为错误而不是常规代码路径来处理。
答案 1 :(得分:1)
std::optional
是一个类,表示“可选”值。如果遇到某种情况,可能会产生一个值,或者可能不会产生一个值,那么可选是可行的方法。性能价格可以忽略不计(附加条件,如果此处的值实际上可能会使速度变慢,但是由于值是可选的,因此您始终必须检查某个位置,如果仍然存在该值),则实际价格就是内存:
printf("%d\n", (int)sizeof(std::string));
printf("%d\n", (int)sizeof(std::optional<std::string>));
打印:
32
40
使用lang 64位。
所以,如果您要这样做-假设一个结构:
struct A {
std::optional<std::string> a, b, c, d, e, f, g, h;
};
struct B {
std::string a, b, c, d, e, f, g, h;
unsigned char has_a : 1,
has_b : 1,
has_c : 1,
has_d : 1,
has_e : 1,
has_f : 1,
has_g : 1,
has_h : 1;
};
然后您将获得sizeofs:
A - 320
B - 264
这实际上可能会造成伤害。但通常不。
那么您滥用可选吗?如果您使用可选信号来指示错误,则可能会略有不同,具体取决于错误的上下文。但这总是取决于。如果要让代码清晰起来更重要,那就是该函数没有产生价值,而不是为什么没有产生价值,那么请肯定。
答案 2 :(得分:0)
我会尝试做一个简短的回答:
std::optional
的核心只是一个布尔值(ala。std::pair<T, bool>
)。请记住,在大多数情况下,原始指针也是可选的。
通常使用以下经验法则:
std::optional<std::string>
,有时反之亦然。答案 3 :(得分:-4)
我会说这是一种滥用。
我将重写为std::string getName() const
(依赖于现在需要的RVO),如果发生不愉快的事情(例如某种获取错误),则引发异常(源自{{1 }})。空名是否是错误取决于您的应用程序。
如果不允许使用异常,则依靠C样式错误传播表格
std::exception
使用int getName(std::string*) const
会使实际错误变得不透明(除非您将错误std::optional
施加到当前线程上)。