8

Using C++ I'd like to do something along the lines of:

  1. Try to get a reference to something in, say, a map
  2. If it throws, then return straight away
  3. Otherwise, go and use the reference

However because we can't declare a reference object without initialising in C++, then we can't do this directly.

For example if I was happy to pass by value, I could do:

std::map<char, int> mymap {...};
int x;
try {
    x = m.at('a');
} catch (out_of_range& e) {
    return;
}
// do something with x

However because I need a reference, I would have to do the following which won't compile.

std::map<char, int> mymap {...};
int& x;
try {
    x = m.at('a');
} catch (out_of_range& e) {
    return;
}
// do something with x

So the options I can see are (1), use a pointer, which I'd like to avoid, or (2), use a different map method which results in more verbose code, such as:

std::map<char, int> mymap {...};
auto x_it = m.find('a');
if (x_it == m.end()) {
    return;
}
int& x = x_it->second;
// do something with x

Are those the only options or, is there another way of doing this neatly?

5
  • 1
    It would be nice to avoid having to make values of the map smart pointers just for this use case.
    – Alex
    Commented Aug 11, 2016 at 2:18
  • 2
    "which results in more verbose code, such as" It's the same number of lines of code, either way. So why do you say that it's more verbose? Indeed, I'd go so far as to say that catch(~something~) return; is an anti-pattern. If you're catching an exception, you had better be doing something more than just returning. Commented Aug 11, 2016 at 3:33
  • It is the same number of lines, but it has more logic, i.e., "is it ok? then do it, otherwise handle problem" vs "do it, but if not OK handle problem." I feel the forgiveness rather than permission style is cleaner. Plus it avoids the risk of the state changing between the check that it's ok to proceed and actually accessing the element.
    – Alex
    Commented Aug 11, 2016 at 4:55
  • 4
    The way your code looks, the exception being thrown is a normal use-case. As such, I would strongly advise against using the throwing member of std::map<>: The performance hit of a thrown exception is quite large (on the order of a micro second, if I remember correctly). You can do quite a lot of useful things in that time. Commented Aug 11, 2016 at 9:48

2 Answers 2

11

The normal way is to move the "so something with x" line(s) also within the try block:

std::map<char, int> mymap {...};
try {
    int& x = mymap.at('a');
    // do something with x
} catch (out_of_range& e) {
    return;
}

The nice thing about exceptions is that when an exception is thrown, all code between the throw and catch locations is completely skipped over.
This means that you can write the code within the try { ... } catch block with the knowledge that everything has worked fine if you reach that line.

2
  • Thanks. This solves the problem, with the only downside being if // do something with x is long, then you end up with a large block of code inside the try block. I guess this can be resolved with some refactoring.
    – Alex
    Commented Aug 11, 2016 at 23:23
  • 2
    @Alex: More relevantly, you'll swallow any out_of_range exceptions that // do something with x throws, which probably isn't what you want.
    – Eric
    Commented Mar 29, 2018 at 19:31
1

If you had wanted to return a reference from both the try and the catch branch, it would be easy:

std::map<char, int> mymap {...};
int &x = [&]() -> int & { try {
    return mymap.at('a');
} catch (std::out_of_range& e) {
    return some_default_value;
} } ();
// do something with x

But in your case, only the try branch returns a reference, so the return type of the entire try block is not a int & but more like optional<int &>. The problem is that C++17 is not out yet.

Nonetheless, optional<int &> is isomorphic to int *, so ultimately using a pointer here seems like the right thing to do. If you don't like the -> syntax, you can just reassign int &y = *x;.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.