ida*_*hmu 3 c++ c++17 stdoptional
std::optional适合我的代码?我的代码包含许多看起来大致如下的函数:
bool GetName(_Out_ string& strRes)
{
// try to get/calculate the value
// value may also be empty
// return true on success, false otherwise.
}
Run Code Online (Sandbox Code Playgroud)
既然我已经找到了std::optional,我只需编写代码:
std::optional<std::string> GetName()
Run Code Online (Sandbox Code Playgroud)
std::optional?std::optional会带来性能价格,为了争论,让我们假设我愿意支付它。std::optional<int> iVal = 9;毫无意义。std::optional?我觉得std::optional很自然,它让我得出一个结论,默认情况下,我的许多本机类型bool, int, string, 应该声明为std::optional.
因此,而不是遵循以下前提:
std::optional仅在绝对需要时使用
我倾向于:
std::optional始终使用,除非您确定现在或将来不需要它。
以下规则是否有效:
使用std::optional时:
如果使用std::optional变得丰富,我是否会冒代码可读性问题的风险?
回答和评论使讨论偏离了我最初的关注点,即试图定义何时使用std::optional.
我将解决一些我收到的常见反馈:
您正在编写 C 风格的错误检查函数。
计算失败时使用异常。
提供一个不太好的例子来说明我的动机可能是我的错。GetName()只是我的常见用例的一种体验。与其关注错误是如何反映的(如果有的话),我想关注的是命名一个好的候选者是可选的吗?
我没有说明如果GetName返回 false 则意味着我的流程中存在某种错误。当GetName返回 false 时,我可以生成一个随机名称或忽略当前的迭代。不管呼叫者如何反应的返回值,name是optional因为它的感觉may not be present。
我可以提供一个更好的例子:
void GetValuesFromDB(vector<string> vecKeys, map<string,string> mapKeyValue);
Run Code Online (Sandbox Code Playgroud)
在这个函数中,我“被迫”提供两个容器,因为我想区分:
mapKeyValuemapKeyValue但optional我可以去:
void GetValuesFromDB(map<string,std::optional<string> > mapKeyValue);
Run Code Online (Sandbox Code Playgroud)
我相信我们对这个词optional过于字面化了。
我真的支持人们可以使用If 你想很好地表示一个可为空类型的概念std::optional 。而不是使用唯一值(如 -1、nullptr、NO_VALUE 或其他东西)
C++ 标准委员会可以很容易地决定将std::optional, std::nullable.
我是否在滥用 std::optional
也许。
的预期语义std::optional是指示某些内容是可选的。也就是说,如果事物不存在,那是可以的,而不是错误。
如果您的方法失败,它将返回一个空的可选项以指示失败。但是这个失败是否也是一个错误取决于调用代码所期望的语义,你没有显示或描述:如果可选结果为空,你会怎么做?
例如,
auto name = GetName();
if (!name) { diediedie(FATAL_ERROR); }
Run Code Online (Sandbox Code Playgroud)
表明该名称并不是真正可选的。如果您不能(如您所说)在此处使用异常,则该variant<string, error>方法似乎更清晰,并且还允许您的失败功能传达失败原因,否则会出现异常。
反过来
auto name = GetName();
if (name) {
cout << "User is called " << *name << '\n';
} else {
cout << "User is anonymous\n";
}
Run Code Online (Sandbox Code Playgroud)
似乎合理,因为无法获取名称不是错误。
为什么...不使用
std::optional?
当您不想传达某些内容是可选的时。
显然,您可以像其他任何事情一样滥用它,但它可能会造成混淆、误导、出乎意料和其他不良做法。
我是不是太着迷了……?
也许。如果您的错误处理污染了您的所有代码,您可能应该考虑尽早失败,而不是绊倒永远不会成功的逻辑。
如果您确实有很多变量可能在法律上丢失了 - 就您的程序逻辑而言 - 那么您可能正在正确使用它。
问题 1
同样,这取决于失败在程序上下文中的含义。
问题2
是的,如果一切都是可选的,您的代码将变得一团糟,部分原因是视觉噪音,但主要是因为您永远无法确定在逻辑中的给定点存在哪些变量。
你的GetValuesFromDB编辑是可能蛮好用的可选的,如果你的数据库是相对松散的,没有限制。您不能依赖于任何特定的列被填充,一切都是可选的,您需要手动检查每个字段的存在。它们真的是可选的。
如果您的数据库是高度结构化的,则可能不适合使用 optional 作为主键。你不能有一排没有一个主键,这样使得它可选似乎有点不可思议。不过,也许对于少数强制性的列有一个单独的界面是不值得的。
我相信我们从字面上理解了“可选”这个词。
我真的不知道你是否阅读了我的回答——你的评论表明你至少不理解它。
假设我们称其为可空的。我仍然建议返回nullable<T>可能合法不存在的东西是好的,但是对于不存在表明错误的东西返回相同的类型是错误的。
您必须决定什么构成您的代码错误。我们无法为您做这件事,尤其是对于所提供的信息。那么你应该对所有事情都使用 optional 吗?也许。这就是我所说的。确定您自己代码的语义和前提条件,您就会知道哪些内容是可选的,哪些内容是强制性的,哪些内容的缺失应作为错误而不是常规代码路径处理。
std::optional是一个类,表示“可选”值。如果你有一个案例,你可能会产生一个值,也可能不会产生一个值,那么可选是一种可行的方法。性能价格可以忽略不计(附加条件,如果值在这里实际上可能会减慢速度,但由于值是可选的,因此您总是必须在某个地方检查值是否存在),真正的价格是内存:
printf("%d\n", (int)sizeof(std::string));
printf("%d\n", (int)sizeof(std::optional<std::string>));
Run Code Online (Sandbox Code Playgroud)
印刷:
32
40
Run Code Online (Sandbox Code Playgroud)
在 clang 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;
};
Run Code Online (Sandbox Code Playgroud)
然后你会得到 sizeofs:
A - 320
B - 264
Run Code Online (Sandbox Code Playgroud)
这实际上可能会造成伤害。但通常不会。
那么你在滥用可选功能吗?如果您使用可选来表示错误,那么可能会稍微是,具体取决于错误的上下文。但这总是要看情况。如果对于代码的清晰度来说,更重要的是要表明该函数没有产生价值,而不是它为什么没有产生价值,那么当然,就去做吧。