2

FindOrNull is so much more concise than the build-in find. Compare

    auto& map = GetMap()
    if (auto iter = map.find("key"); iter != map.end()) {
       // use iter.second
    }

With

    if (auto value = GetMap().find_or_null("key")) {
       // use *value
    }

yufanyufan
  • 171
  • 1
  • 7
  • 2
    Since it can contain values which can't be represented as null. – Marek R Feb 07 '20 at 17:27
  • 1
    What do you expect a `null`object to be, a default-constructed one? That will create ambiguities if the map actually contains such an object. – perivesta Feb 07 '20 at 17:27
  • 1
    You should propose it. Now that we have `std::optional` it would be a nice addition. – NathanOliver Feb 07 '20 at 17:27
  • 4
    C++ doesn't have a "null" value. What would the "null" value be of some user-defined structure? – Some programmer dude Feb 07 '20 at 17:27
  • 1
    You mean, return an iterator that can be used like `it == NULL`? That inflicts a restriction on iterators that they must be comparable to integer and to `std::nullptr_t`, which would make implementation much more difficult. – Yksisarvinen Feb 07 '20 at 17:29
  • It would not make sense for types that do not have a `null` == *invalid* value – Michael Chourdakis Feb 07 '20 at 17:29
  • This would make sense if the method returned a _pointer to_ the found element. That would be similar to the function [`std::get_if`](https://en.cppreference.com/w/cpp/utility/variant/get_if) used with `std::variant`. – alter_igel Feb 07 '20 at 17:30
  • Your proposed syntax does not accurately distinguish between "there is no find result" and "there is a find result which has a value of false." – Kenny Ostrom Feb 07 '20 at 17:33
  • Related: [Is it!=container.end() design mistake/feature or just necessity?](https://stackoverflow.com/questions/12749899/is-it-container-end-design-mistake-feature-or-just-necessity) – JaMiT Feb 07 '20 at 17:41
  • What should `find_or_null` return if it doesn't find the key? – Werner Henze Feb 07 '20 at 17:42
  • I think OP means that the return type of `find_or_null` should be `map::value_type *`, so it would be `nullptr` if the key is not present and a pointer to the value if the key is present. This matches the behavior of `std::get_if` for `std::variant` and `std::any_cast` for `std::any *`. – n314159 Feb 07 '20 at 18:03
  • It is easy to implement this in a stand alone template/function using the existing interface for a map (`FindKeyOrNull(GetMap(), "key")`). You'd also have control over what, exactly, to return if the key wasn't found. – 1201ProgramAlarm Feb 07 '20 at 19:21

3 Answers3

2

First of all std::map can contain anything. It can be a pointer (then null makes sense, but it contain a value then null has no meaning).

To overcome this you can just use a helper:

template<typename T, typename K>
auto get_optional_value(T&& map, K&& key)
{
    auto it = map.find(std::forward<K>(key));
    if (it == map.end()) return std::optional<typename std::decay<T>::type::mapped_type>{};
    return std::optional<typename std::decay<T>::type::mapped_type>{it->second};
}

https://wandbox.org/permlink/TS56MAb97NQXkepj

Note that std::optional is quite fresh c++17.

zdan
  • 28,667
  • 7
  • 60
  • 71
Marek R
  • 32,568
  • 6
  • 55
  • 140
  • and now that it is 4 years we have `std::optional`, the question becomes to "why doesn't `std::map` provide a `find()` member that returns a `std::optional` " ? – Grim Fandango Nov 25 '20 at 18:19
1

Your proposal does not accurately distinguish between "not found" and "found a value of null/false/0". Currently c++ gets that distinction by comparing to container.end() You're going to need to get that info somehow.

Going by the example of a database, we need "null" to be completely distinct from any actual values. Here, you seem to be equating it with false, nullptr or 0. All of which are valid values within some container that would have to implement this interface.

Going by some examples from python, the "not found" option is an exception. That's easy to handle in python because the language makes performance sacrifices to make exceptions relatively easy to process. But handling it with an exception in c++ is far worse performance, and is significantly worse from a syntax clarity standpoint as well.

Your proposal is closer to a getter which provides a default value (e.g. false, 0, nullptr). You lose the information about whether it was found in the container, but that is not always needed. That's something you can implement in your class, but do you want to require it be implemented for all containers in the standard library? That's the question here.

Kenny Ostrom
  • 5,639
  • 2
  • 21
  • 30
0

I think you are asking why the STL does not provide a function

template<class K, class V>
V* map<K,V>::find_or_null(const K& key) {
    auto it = find(key);
    if(it)
         return &it->second;
    else
         return nullptr;
}

Questions about why the standard library does something have often an answer that will not satisfy you. There are always multiple ways to do something and the standard library chooses one. I personally don't see that much of a need for your proposed function. It saves not much vs the find way and it looses the information on the key. Note that the key in the map is only equivalent to the looked-up key and not necessarily equal. So this information is interesting.

If you want to compare this to the functions where the library goes this way (std::get_if for variants and std::any_cast for std::any *) you will see that there was a reason to do this. These are the only ways to access those containers without possibly throwing if you don't do the checking in advance. For map you already have this with find, where it returns an iterstor that is pretty similar to a pointer only with extra information.

n314159
  • 4,990
  • 1
  • 5
  • 20