简体   繁体   中英

What function signature should I use to return a reference to an object that might not exist?

I'm writing a simple container class in C++ similar to a map that stores objects indexed by a key. I'd like to provide an accessor function such as:

V& getValue(const K &key);

where I return a reference to the value.

But I also wanted to handle the case where the key/value is not present and be able to return some status to the user (there could be some reasons why it is not there that I want to communicate back to the caller via some Status type).

I suppose I could do the following but calling this would require a V object to be constructed before this function can be called, and I'm just going to copy the internal V object into the one being passed in by reference, so that seems bad.

Status getValue(const K &key, V& v);

I could also do:

V &getValue(const K &key, Status &s);

But for some reason that seems a little bit clunky as focus is drawn away from the Status and users may forget to check it (but maybe that's not my problem).

So is there anyway to have a function similar to

Status getValue(const K &key, V& v);

without the a dummy V object needing to be constructed before calling it? You can't pass a reference to a reference. I suppose I could use pointers and am happy to do so, but it is less desirable for creating a simple to use and reason about function.

Any thoughts?

The usual solution is to provide a bool contains(const K &key) function and if you don't want your accessor to silently create entries, have it throw an exception instead. (having two accessors, one that throws an exception, and another that creates entries is also common)

Of course, this may not be what you want. What is the reason you want to fit all this into a single function?

You could go with your original function V& getValue(const K &key) , and throw an exception to indicate non-successful Status values.

Then, callers have the option of delaying how they handle the problem (by letting the exception bubble up to an appropriate handler), but they cannot outright forget about it because the program will crash otherwise. The call site needn't be cluttered with status-checking and error-reporting code.

I would go with boost::optional or create a similar template if you don't want to use boost. With boost::optional your function will look like this:

boost::optional<MyClass &> getValue(const K &key)

I see 3 options:

  1. Return a "wrapper" object that can be converted to V& and can tell whether it stores a valid reference or not (can be implemented around V*).
  2. Return a pointer.
  3. Return a reference and throw and exception if the key is not found.

I personally would go with a pointer.

考虑返回boost::variant<V &, Status>或(等效地) boost::variant<std::reference_wrapper<V>, Status>

I would follow an idea from std::map interface:

std::pair<iterator,bool> getValue(const K& key);

The value object is accessible through the iterator, the bool flag shows if the value was present.

First I'll answer your question as written: I would suggest returning a boost::optional<boost::ref<V> > so that you can still return by reference, but optionally so.

However, I wouldn't suggest this interface. The standard containers already have an interface that solves this exactly problem, namely returning iterators rather than values. I would suggest just using an "iterator" interface (it doesn't have to be a real full-blown iterator) and returning an end /one past the end to indicate that the iter is not found, rather than returning by reference or not. The more you make your container's interface look like a standard container, the greater your options for using standard algorithms AND plugging it in anywhere you want as a substitution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM