Question

With the advent of C++11, we have unordered_map.cbegin/cend to specifically return us values of const_iterator. so the deduced type of 'it' in the expression "auto it = unordered_map.cbegin()" is const_iterator.

However, when it comes to unordered_map.find(key) function, I think there may be missing a "cfind()" counterpart, which returns a const_iterator specifically.

Some say that we can use "const auto it = unordered_map.find(key)" to obtain a "const iterator", but I have a strong suspicion that "const iterator" is the same "const_iterator", where "const iterator" limits the ability to change the iterator itself, while "const_iterator" limits the ability to change the content the iterator is referring to.

So, really, if we want to take advantage of "auto" type deduction fully (with the knowledge of the confusions or the variations of "auto" type deduction - auto, auto&, const auto&, etc.), how can I have unordered_map.find(key) to return a "const_iterator" without me having to explicitly specify "const_iterator" - that's after all the best use case for auto!

Below is a simple example code that demonstrates the compiler behavior:

#include "stdafx.h"
#include <unordered_map>

int _tmain(int argc, _TCHAR* argv[])
{
    typedef std::unordered_map<int, int> umiit;
    umiit umii;

    auto it0 = umii.find(0);
    it0->second = 42;
    const auto it1 = umii.find(0);
    it1->second = 42;
    umiit::const_iterator it2 = umii.find(0);
    it2->second = 42; // expected compiler error: assigning to const

    return 0;
}
Was it helpful?

Solution

I'm not aware of any place that takes a const_iterator where you couldn't simply pass an iterator instead, so this deficiency may not interfere much with day-to-day code writing. However, I do prefer to use const_iterators (and const in general) wherever I don't need mutating, in the interests of general communication, so I think adding a cfind() might be a useful addition to the future standard library.

I think this code could function as a simple workaround for what you're trying to achieve, though:

template<typename T>
auto use_as_const( T const &t ) -> T const & {
    return t;
}

This is a simple casting wrapper function, similar in style to move() and forward<T>(), to provide (and document) a constraint on individual usages of the object. You could then use it like this:

auto it1 = use_as_const( umii ).find(0);

This could also be used instead of leaning on cbegin() and cend(). Or, it could be used in range-based for loops:

for ( auto &element : use_as_const( some_vector_of_string ) ) {
    cout << element;
    // element = ""; // This line shouldn't compile.
}

In the above loop example, although I would generally prefer auto const &element : ..., I believe it would be unnecessary and element would still be deduced to be a const reference.

OTHER TIPS

It's a bit of a deficiency; we have cbegin and cend but no corresponding cfind, etc.

I'd suggest using a utility function to get a const reference to the object, as per the answer to forcing use of cbegin()/cend() in range-based for:

template<typename T> constexpr const T &as_const(T &t) { return t; }

auto it1 = as_const(umii).find(0);
it1->second = 42; // fails
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top