Reputation: 9788
You probably know situations like this where you just want to assign to a (const
) variable with an expression which might fail (throw) (e.g.container.at()
) which forces you to write boiler plate code:
void foo(const string &key) {
auto it = data_store.find(key);
if (it == data_store.end()) {
return;
}
const auto & element = it->second;
...
go on with `element`...
...
}
In Python you could write code like this:
def foo(name):
try:
element = data_store[key]
except KeyError:
return
..
go on with `element`
..
.. with is less noisy because you don't introduce that useless extra it
just for checking existence.
If C++'s try
would not introduce a variable scope you could just use at()
:
void foo(const string &key) {
try {
const auto & element = data_store.at(key);
} catch (const out_of_range &) {
return;
}
...
go on with `element`...
...
}
What's the way to go here if you don't want to abandon constness and keep your code clean?
If lambdas only could have a try
/catch
body you could write
void foo(const string &key) {
const auto & element = [&] () -> T try {
return data_store.at(key);
} catch () {
return;
} ();
...
go on with `element`...
...
}
Some answers to similar questions suggest try
/catch
blocks around all the code:
void foo(const string &key) {
try {
const auto & element = data_store.at(key);
...
go on with `element`...
...
} catch (const out_of_range &) {
return;
} catch (some other exception) {
...
} catch (some other exception) {
...
}
}
But I don't like this because of three reasons:
at()
and it's catch
blockout_of_range
Which (nice, short and clean) alternatives do you know?
Upvotes: 5
Views: 387
Reputation: 141628
There are three good options on this thread, there's not really any other option.
Those cases assume we are initializing an object; for initializing a reference as you are, apply the techniques to std::reference_wrapper
, or a pointer.
BTW I would not discount your first code sample so quickly. It's simpler than all the other options, and it is a common recommendation in C++ to only use exceptions for exceptional conditions -- things you do not expect to be a normal part of the function's contract. It's not idiomatic to use them as a shortcut.
In other words, if the function design is to do nothing if the lookup fails, then throw-catching is an unnecessary complication to the function. You've just written an even uglier version of C-style error handling.
The whole point of the at()
accessor is that your function can be kept simple by not catching -- the exception can be left to propagate up to a more general error handler.
Upvotes: 2