Reputation: 279
In a game I would like to search a map of items and return the one located on a particular square of the board. But what if the square is empty? (The items are not stored in the board structure. Never mind about that for the purposes of this question.) I have the code below, but what should I do to return an "empty" reference?
map<pair<int, int>, Item*> _items;
Item& itemAt(int row, int col) const {
try {
return *_items.at(make_pair(row, col));
} catch(out_of_range& e) {
return // what goes here?
}
}
Or is this the wrong approach and I should just use find()
?
Upvotes: 8
Views: 2419
Reputation: 25641
Actually other people have had the same problem:
boost::optional
Upvotes: 1
Reputation: 122001
If it is an error for client code to request an item that does not exist then throw
an exception to report the failure and return an Item&
.
If it is not an error, as the value_type
of the map
is already an Item*
change the return type to be a Node*
and return nullptr
to indicate that at item at the requested position does not exist and use map::find()
.
To avoid lifetime problems, which already exist with Item&
return type, consider changing the value_type
to be a std::shared_ptr<Item>
. If client code has a reference or a raw pointer to a value with the map
and that element is removed from the map
then the client is left with a dangling pointer/reference. Switching to a std::shared_ptr<Item>
avoids this scenario. The itemAt()
function would return a std::shared_ptr<Item>
. This also has the benefit that the Item
s in the map
do not need to be explicitly delete
d.
Upvotes: 3
Reputation: 456
In this case, using a pointer as a means to represent "zero or one object" is useful:
Item* itemAt(int row, int col) const {
try {
return _items.at(make_pair(row, col));
} catch(out_of_range& e) {
return nullptr;
}
}
However, using std::map::find()
is probably a faster and cleaner approach.
Upvotes: 9
Reputation: 126502
If not finding an item is not an error condition in your program, then you should not return a reference (since references cannot be null). Rather, you should return a (non-owning, most likely) pointer, and return nullptr
in case the item was not found:
Item* itemAt(int row, int col) const {
try {
return _items.at(make_pair(row, col));
} catch(out_of_range& e) {
return nullptr;
}
}
On the other hand, if not finding an item is an error, then you can return a reference (when the item is found) and let the exception propagate when the item is not found - the responsibility of handling it would belong to the part of your code that has strategic knowledge on how to handle it:
Item& itemAt(int row, int col) const {
return *_items.at(make_pair(row, col));
}
Upvotes: 12