If I have a find
function that can sometimes fail to find the required thing, I tend to make that function return a pointer such that a nullptr
indicates that the thing was not found.
如果我有一个find函数,有时候找不到所需的东西,我倾向于让该函数返回一个指针,以便nullptr指示找不到该东西。
E.g.
Student* SomeClass::findStudent(/** some criteria. */)
If the Student exists, it will return a pointer to the found Student
object, otherwise it will return nullptr
.
如果Student存在,它将返回指向找到的Student对象的指针,否则它将返回nullptr。
I've seen boost::optional
advocated for this purpose as well. E.g. When to use boost::optional and when to use std::unique_ptr in cases when you want to implement a function that can return "nothing"?
我见过boost :: optional也是为了这个目的而提倡的。例如。何时使用boost :: optional以及何时使用std :: unique_ptr,如果你想实现一个可以返回“nothing”的函数?
My question is, isn't returning a pointer the best solution in this case. i.e. There is a possibility that the queried item will not be found, in which case returning nullptr is a perfect solution. What is the advantage of using something like boost::optional
(or any other similar solution)?
我的问题是,在这种情况下,返回指针不是最佳解决方案。即,有可能找不到查询的项目,在这种情况下返回nullptr是一个完美的解决方案。使用boost :: optional(或任何其他类似解决方案)之类的优势是什么?
Note that, in my example, findStudent
will only ever return a pointer to an object that is owned by SomeClass
.
请注意,在我的示例中,findStudent将只返回指向SomeClass所拥有的对象的指针。
4 个解决方案
#1
9
The advantage of an optional<Student&>
return type here is that the semantics of usage are readily apparent to all users that are familiar with optional
(and will become readily apparent once they familiarize themselves with it). Those semantics are:
这里可选的
- The caller does not own the
Student
and is not responsible for memory management. The caller simply gets a reference to an existing object. - It is clear that this function can fail. You maybe get a value and you maybe get nothing. It is clear that the caller needs to check the result one way or the other.
调用者不拥有Student并且不负责内存管理。调用者只是获取对现有对象的引用。
很明显,这个功能可能会失败。你可能得到一个价值,你可能什么也得不到。很明显,调用者需要以某种方式检查结果。
optional<T>
is self-documenting in a way that T*
isn't. Moreover, it has other benefits in that it can work in cases where you want to return any kind of object type without the need for allocation. What if you needed to return an int
or double
or SomePOD
?
可选
#2
4
optional<T&>
was removed from the C++ standardization track because its use is questionable: it behaves nearly identically to a non-owning T*
with slightly different (and confusingly different from optional<T>
and T*
) semantics.
从C ++标准化轨道中删除了可选的
optional<T&>
is basically a non-owning T*
wrapped up pretty, and somewhat strangely.
可选的
Now, optional<T>
is a different beast.
现在,可选
I have used optional<Iterator>
in my container-based find algorithms. Instead of returning end()
, I return the empty optional. This lets users determine without a comparison if they have failed to find the item, and lets code like:
我在基于容器的查找算法中使用了可选的
if(linear_search_for( vec, item))
work, while the same algorithm also lets you get at both the item and the location of the item in the container if you actually need it.
同时,如果您确实需要,同样的算法还可以让您同时获取容器中项目和项目的位置。
Pointers to elements doesn't give you the location information you might want except with contiguous containers.
除了连续的容器外,指向元素的指针不会提供您可能需要的位置信息。
So here, I've created a nullable iterator that has the advantages of iterators (generically working with different types of containers) and pointers (can be tested for the null state).
所以在这里,我创建了一个可以为空的迭代器,它具有迭代器(通常使用不同类型的容器)和指针(可以测试null状态)的优点。
The next use is actually returning a value. Suppose you have a function that calculates a rectangle.
下一个用途实际上是返回一个值。假设您有一个计算矩形的函数。
Rect GetRect();
now, this is great. But what if the question can be meaningless? Well, one approach is to return an empty rect or other "flag" value.
现在,这很棒。但如果这个问题毫无意义呢?好吧,一种方法是返回一个空的rect或其他“flag”值。
Optional lets you communicate that it can return a rect, or nothing, and not use the empty rect for the "nothing" state. It makes the return value nullable.
可选允许您通知它可以返回一个rect或什么都没有,并且不使用空rect来表示“nothing”状态。它使返回值可以为空。
int GetValue();
is a better example. An invalid value could use a flag state of the int -- say -1
-- but that forces every user of your function to look up and track the flag state, and not accidentally treat it as a normal state.
是一个更好的例子。无效值可以使用int的标志状态 - 比如-1 - 但这会强制函数的每个用户查找并跟踪标志状态,而不会意外地将其视为正常状态。
Instead, optional<int> GetValue()
makes it clear that it can fail, and what the failure state it. If it is populated, you know it is a real value, and not a flag value.
相反,可选的
In both of these cases, returning a non-owning pointer is non-viable, because who owns the storage? Returning an owning pointer is expensive, because pointless heap allocations are pointless.
在这两种情况下,返回非拥有指针是不可行的,因为谁拥有存储?返回拥有指针是昂贵的,因为无意义的堆分配是没有意义的。
Optionals are nullable value types. When you want to manage resources locally, and you still want an empty state, they make it clear.
Optionals是可以为空的值类型。当您想在本地管理资源,并且仍然需要一个空状态时,他们会明确说明。
Another thing to look into is the expected
type being proposed. This is an optional, but when in the empty state contains a reason why it is empty.
另一个需要考虑的是预期的类型。这是一个可选项,但是当处于空状态时包含它为空的原因。
#3
3
optional<T&>
may indeed be replaced by T*
but T*
has not clear semantic (ownership ?).
可选的
But optional<T>
cannot be replaced by T*
. For example:
但是可选的
optional<Interval> ComputeOverlap(const Interval&, const Interval&);
If there is no overlap, no problem with T*
(nullptr
) or optional<T>
. But if there is an overlap, we need to create a new interval. We may return a smart_pointer in this case, or optional.
如果没有重叠,则T *(nullptr)或可选
#4
1
Lets consider you have a std::map<IndexType, ValueType>
where you are trying to find something (Note: The same applies for other containers, this is just to have an example). You have these options:
让我们考虑你有一个std :: map
- You return a
ValueType&
: The user can modify your map-content and does not need to think about memory-allocation/deallocation. But if you dont find anything in your map, you need to throw an exception or something similar. - You return a
ValueType*
: The user can modify your map-content and you can return a nullptr if you dont find anything. But the user can call delete on that pointer and you must specify anyhow if he has to do so or not. - You return a smart pointer to
ValueType
: The user does not have to worry about delete or not-delete and can modify your map-content depending on the type of smart-pointer. You can also return a nullptr. But this pretty much requires you to deal with smart_pointers in your map, which is overly complicated ifValueType
would be e.g. just an int otherwise. - You return a simple
ValueType
: The user can not modify your map-content and does not need to think about memory-allocation/deallocation. But if you dont find anything in your map, you need to return some specialValueType
which tells the user you didn't find anything. In case yourValueType
is e.g.int
, which one would you return that makes clear "no int found". - You return a boost::optional, which is the closest you can get to a simple
ValueType
return by value with the additional option of "not returning aValueType
"
您返回ValueType&:用户可以修改您的地图内容,而不需要考虑内存分配/释放。但是如果你在地图中找不到任何东西,你需要抛出异常或类似的东西。
您返回ValueType *:用户可以修改您的地图内容,如果您没有找到任何内容,则可以返回nullptr。但是用户可以在该指针上调用delete,无论如何他必须指定是否必须这样做。
您返回一个指向ValueType的智能指针:用户不必担心删除或不删除,并可以根据智能指针的类型修改您的地图内容。您还可以返回nullptr。但这几乎要求你在你的地图中处理smart_pointers,如果ValueType是例如,那就过于复杂了。只是一个int否则。
您返回一个简单的ValueType:用户无法修改您的地图内容,也不需要考虑内存分配/释放。但是如果您在地图中找不到任何内容,则需要返回一些特殊的ValueType,告诉用户您没有找到任何内容。如果你的ValueType是例如int,你会返回哪一个明确表示“没有找到int”。
你返回一个boost :: optional,这是你最接近一个简单的ValueType返回值,附加选项是“不返回ValueType”
#1
9
The advantage of an optional<Student&>
return type here is that the semantics of usage are readily apparent to all users that are familiar with optional
(and will become readily apparent once they familiarize themselves with it). Those semantics are:
这里可选的
- The caller does not own the
Student
and is not responsible for memory management. The caller simply gets a reference to an existing object. - It is clear that this function can fail. You maybe get a value and you maybe get nothing. It is clear that the caller needs to check the result one way or the other.
调用者不拥有Student并且不负责内存管理。调用者只是获取对现有对象的引用。
很明显,这个功能可能会失败。你可能得到一个价值,你可能什么也得不到。很明显,调用者需要以某种方式检查结果。
optional<T>
is self-documenting in a way that T*
isn't. Moreover, it has other benefits in that it can work in cases where you want to return any kind of object type without the need for allocation. What if you needed to return an int
or double
or SomePOD
?
可选
#2
4
optional<T&>
was removed from the C++ standardization track because its use is questionable: it behaves nearly identically to a non-owning T*
with slightly different (and confusingly different from optional<T>
and T*
) semantics.
从C ++标准化轨道中删除了可选的
optional<T&>
is basically a non-owning T*
wrapped up pretty, and somewhat strangely.
可选的
Now, optional<T>
is a different beast.
现在,可选
I have used optional<Iterator>
in my container-based find algorithms. Instead of returning end()
, I return the empty optional. This lets users determine without a comparison if they have failed to find the item, and lets code like:
我在基于容器的查找算法中使用了可选的
if(linear_search_for( vec, item))
work, while the same algorithm also lets you get at both the item and the location of the item in the container if you actually need it.
同时,如果您确实需要,同样的算法还可以让您同时获取容器中项目和项目的位置。
Pointers to elements doesn't give you the location information you might want except with contiguous containers.
除了连续的容器外,指向元素的指针不会提供您可能需要的位置信息。
So here, I've created a nullable iterator that has the advantages of iterators (generically working with different types of containers) and pointers (can be tested for the null state).
所以在这里,我创建了一个可以为空的迭代器,它具有迭代器(通常使用不同类型的容器)和指针(可以测试null状态)的优点。
The next use is actually returning a value. Suppose you have a function that calculates a rectangle.
下一个用途实际上是返回一个值。假设您有一个计算矩形的函数。
Rect GetRect();
now, this is great. But what if the question can be meaningless? Well, one approach is to return an empty rect or other "flag" value.
现在,这很棒。但如果这个问题毫无意义呢?好吧,一种方法是返回一个空的rect或其他“flag”值。
Optional lets you communicate that it can return a rect, or nothing, and not use the empty rect for the "nothing" state. It makes the return value nullable.
可选允许您通知它可以返回一个rect或什么都没有,并且不使用空rect来表示“nothing”状态。它使返回值可以为空。
int GetValue();
is a better example. An invalid value could use a flag state of the int -- say -1
-- but that forces every user of your function to look up and track the flag state, and not accidentally treat it as a normal state.
是一个更好的例子。无效值可以使用int的标志状态 - 比如-1 - 但这会强制函数的每个用户查找并跟踪标志状态,而不会意外地将其视为正常状态。
Instead, optional<int> GetValue()
makes it clear that it can fail, and what the failure state it. If it is populated, you know it is a real value, and not a flag value.
相反,可选的
In both of these cases, returning a non-owning pointer is non-viable, because who owns the storage? Returning an owning pointer is expensive, because pointless heap allocations are pointless.
在这两种情况下,返回非拥有指针是不可行的,因为谁拥有存储?返回拥有指针是昂贵的,因为无意义的堆分配是没有意义的。
Optionals are nullable value types. When you want to manage resources locally, and you still want an empty state, they make it clear.
Optionals是可以为空的值类型。当您想在本地管理资源,并且仍然需要一个空状态时,他们会明确说明。
Another thing to look into is the expected
type being proposed. This is an optional, but when in the empty state contains a reason why it is empty.
另一个需要考虑的是预期的类型。这是一个可选项,但是当处于空状态时包含它为空的原因。
#3
3
optional<T&>
may indeed be replaced by T*
but T*
has not clear semantic (ownership ?).
可选的
But optional<T>
cannot be replaced by T*
. For example:
但是可选的
optional<Interval> ComputeOverlap(const Interval&, const Interval&);
If there is no overlap, no problem with T*
(nullptr
) or optional<T>
. But if there is an overlap, we need to create a new interval. We may return a smart_pointer in this case, or optional.
如果没有重叠,则T *(nullptr)或可选
#4
1
Lets consider you have a std::map<IndexType, ValueType>
where you are trying to find something (Note: The same applies for other containers, this is just to have an example). You have these options:
让我们考虑你有一个std :: map
- You return a
ValueType&
: The user can modify your map-content and does not need to think about memory-allocation/deallocation. But if you dont find anything in your map, you need to throw an exception or something similar. - You return a
ValueType*
: The user can modify your map-content and you can return a nullptr if you dont find anything. But the user can call delete on that pointer and you must specify anyhow if he has to do so or not. - You return a smart pointer to
ValueType
: The user does not have to worry about delete or not-delete and can modify your map-content depending on the type of smart-pointer. You can also return a nullptr. But this pretty much requires you to deal with smart_pointers in your map, which is overly complicated ifValueType
would be e.g. just an int otherwise. - You return a simple
ValueType
: The user can not modify your map-content and does not need to think about memory-allocation/deallocation. But if you dont find anything in your map, you need to return some specialValueType
which tells the user you didn't find anything. In case yourValueType
is e.g.int
, which one would you return that makes clear "no int found". - You return a boost::optional, which is the closest you can get to a simple
ValueType
return by value with the additional option of "not returning aValueType
"
您返回ValueType&:用户可以修改您的地图内容,而不需要考虑内存分配/释放。但是如果你在地图中找不到任何东西,你需要抛出异常或类似的东西。
您返回ValueType *:用户可以修改您的地图内容,如果您没有找到任何内容,则可以返回nullptr。但是用户可以在该指针上调用delete,无论如何他必须指定是否必须这样做。
您返回一个指向ValueType的智能指针:用户不必担心删除或不删除,并可以根据智能指针的类型修改您的地图内容。您还可以返回nullptr。但这几乎要求你在你的地图中处理smart_pointers,如果ValueType是例如,那就过于复杂了。只是一个int否则。
您返回一个简单的ValueType:用户无法修改您的地图内容,也不需要考虑内存分配/释放。但是如果您在地图中找不到任何内容,则需要返回一些特殊的ValueType,告诉用户您没有找到任何内容。如果你的ValueType是例如int,你会返回哪一个明确表示“没有找到int”。
你返回一个boost :: optional,这是你最接近一个简单的ValueType返回值,附加选项是“不返回ValueType”