C++ 我在滥用性病吗 std::optional如何适合我的代码?
我的代码包含许多函数,大致如下所示:C++ 我在滥用性病吗 std::optional如何适合我的代码?,c++,c++17,stdoptional,C++,C++17,Stdoptional,我的代码包含许多函数,大致如下所示: 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::string> GetName() 在这个函数中,我“被迫”提
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::string> GetName()
在这个函数中,我“被迫”提供两个容器,因为我想区分:
mapKeyValue
mapKeyValue
可选的我可以:
void GetValuesFromDB(map<string,std::optional<string> > mapKeyValue);
void GetValuesFromDB(map-mapKeyValue);
我相信我们把这个词理解得太字面了
我真的支持可以使用std::optional
C++标准委员会可以轻易地决定<代码> STD::可选< /COD>,<代码> STD::可空< <代码> >
std::optional
是一个表示“可选”值的类。如果您有一个案例,您可能会产生一个值,也可能不会,那么可选是一种方法。性能价格可以忽略不计(附加条件,如果值在这里,可能实际上会减慢速度,但由于值是可选的,您将始终需要在某个地方进行检查,如果值仍然存在),真正的价格是内存:
printf("%d\n", (int)sizeof(std::string));
printf("%d\n", (int)sizeof(std::optional<std::string>));
在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;
};
这可能真的很痛。但通常不会
那么你是在滥用自由选择权吗?如果您使用optional来表示错误,那么根据错误的上下文,您可能会选择稍微“是”。但这要视情况而定。如果代码清晰性更重要的是信号,那么该函数没有产生值,而不是为什么它没有产生值,那么当然,去做吧。我认为这是一种滥用
我将重写为std::string getName()const
(依赖于现在所需的RVO),如果发生了不正常的情况(例如某种获取错误),则抛出异常(来自std::exception
)的自定义类型)。空白名称是否为错误取决于您的应用程序
如果不允许使用异常,则依赖于表单的C样式错误传播
int getName(std::string*) const
因为使用std::optional
会使实际错误不透明(除非将错误struct
嵌入到当前线程中)
我在滥用性病吗
也许吧
std::optional
的预期语义是表示某些内容是可选的。也就是说,如果事物不存在,那是正常的,而不是错误
如果您的方法失败,它将返回一个空的可选值以指示失败。但是这个失败是否也是一个错误取决于调用代码所期望的语义,您没有显示或描述:如果可选结果为空,您会怎么做
比如说,
auto name = GetName();
if (!name) { diediedie(FATAL_ERROR); }
表明该名称实际上不是可选的。如果您不能(如您所说)在这里使用异常,那么variant
方法似乎更清晰,并且还允许您的失败函数传达异常中可能出现的失败原因
相反地
auto name = GetName();
if (name) {
cout << "User is called " << *name << '\n';
} else {
cout << "User is anonymous\n";
}
auto name=GetName();
如果(姓名){
cout我将试着做一个简短的回答:
在它的核心,一个std::optional
只是一个带有bool的值(ala.std::pair
)。请记住,原始指针(在大多数上下文中)也是可选的
通常使用以下经验法则:
对错误/故障状态/异常情况等使用异常
仅在需要时使用可选功能!除非您是“第二方或第三方”图书馆作者,否则不要计划使用optionals进行futureproof
如果您希望得到结果,请不要使用可选选项
当某些东西实际上是可选的时,使用optional-例如,一个可能最终找到0或1个结果的函数返回一个可选结果是有意义的。一个可能找到多个结果的函数自然会倾向于返回一个结果容器
关于字符串,有时更倾向于使用空字符串(主要是因为简单),而不是std::optional
,有时则相反
当计算失败时,为什么不使用异常?在预期结果时不应使用异常。具有C++风格的错误检查的许多C++函数看起来像代码气味。如果这些函数在成功时返回false,以指示没有检索到任何值(即使没有发生错误)。然后,std::optional
可能是一个合理的选择。唯一的缺点是,一旦成功,您将始终创建一个新对象,而不是重用作为参数传递的现有对象。这可能会导致显著的性能损失。如果您想知道e的原因,另一个选项是类似于std::variant
错误。“我没有声明如果GetName返回false,则表示我的流中存在某种错误。当GetName返回false时,我可以生成一个随机名称,或者忽略当前迭代。”因此,这是一个错误,您知道从该错误中恢复的适当方法。空名称不是错误的标志。它是一个有效的、可行的值。任何字符串值都是有效的,除非计算/获取失败。此外,我们不处理异常,从技术上讲也不能处理异常。它可能是错误的/糟糕的,但我认为一个约束。@ IdSuMu是一个在你的问题中没有被指定的约束。是的,在C中,但是这是C++。你需要函数的用户通过指针传递STD::String,它表示这个函数的STD::String参数是可选的。你不发出信号,函数可能会或可能不填充这个参数。由于一个异常不是“C++方式”。返回一个可选的(可能是有效的)根本不存在的东西是完全好的。在禁止异常的情况下,没有理由去C++中的“完全C”。@ RADOS.AW的建议仍然是一个很好的建议。你做什么?
int getName(std::string*) const
auto name = GetName();
if (!name) { diediedie(FATAL_ERROR); }
auto name = GetName();
if (name) {
cout << "User is called " << *name << '\n';
} else {
cout << "User is anonymous\n";
}