68

First question: is it possible to "force" a const_iterator using auto? For example:

std::map<std::string, int> usa;
//...init usa
auto city_it = usa.find("New York");

I just want to query, instead of changing anything pointed by city_it, so I'd like to have city_it to be map<int>::const_iterator. But by using auto, city_it is the same to the return type of map::find(), which is map<int>::iterator. Any suggestion?

starriet
  • 2,565
  • 22
  • 23
virtualPN
  • 936
  • 1
  • 6
  • 8
  • You want a variable of a specific type without specifying the type? Sorry, it can't be done easily in this case. – Mooing Duck Mar 05 '13 at 20:20
  • 2
    possible duplicate of [How to select iterator type using auto variable](http://stackoverflow.com/questions/9453383/how-to-select-iterator-type-using-auto-variable) – ks1322 Jul 01 '13 at 12:38
  • Maybe `static_cast const &>(usa).find("New York")`? – Kerrek SB Jul 01 '13 at 21:11
  • 1
    `map` is an error (if you mean `std::map`). You must specify at least two template parameters; the key type and the value type. – M.M Sep 11 '15 at 01:38
  • "const auto" is OK in many situations so it's not that absurd – QuentinUK Jan 14 '16 at 03:39
  • 1
    Even though `const auto` may be ok depending on your needs it should have been possible to do `auto city_it = usa.cfind("New York")` to be consistent with `cbegin()`, `cend()` etc... but it was probably forgotten. Your question will hopefully be a reminder and one day we will have `std::cfind`. – Patrick Fromberg Nov 26 '17 at 18:44
  • @QuentinUK How "const auto" is OK if you *can* still modify the object(pointed to by the iterator)? "const auto" only makes the iterator itself const, doesn't it? – starriet Oct 06 '22 at 03:01
  • 1
    @PatrickFromberg Do you mean `std::map<...>::cfind`? Because we don't need `std::cfind` as we can use `cbegin()` and `cend()`. – starriet Oct 06 '22 at 03:09
  • @starriet You can do `auto const & cs = example; auto cit = cs.find(2);` then cit is a const_iterator and the thing being pointed to can't be changed. – QuentinUK Oct 06 '22 at 22:53

8 Answers8

41

Sorry, but I just think the best suggestion is not using auto at all, since you want to perform a (implicitly valid) type conversion. auto is meant for deducing the exact type, which is not what you want here.

Just write it this way:

std::map<std::string, int>::const_iterator city_it = usa.find("New York");

As correctly pointed out by MooingDuck, using type aliases can improve the readability and maintainability of your code:

typedef std::map<std::string, int> my_map;
my_map::const_iterator city_it = usa.find("New York");
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • 5
    Or, if for any obscure reason someone hates typedefs, there is still `decltype(usa.cbegin()) it = ...` ;) – us2012 Mar 05 '13 at 20:25
  • 2
    @us2012: Yes, that would work, but I wouldn't be able to decide between `cbegin()` and `cend()` :-D Just kidding – Andy Prowl Mar 05 '13 at 20:26
27

Since C++17 you can use std::as_const like this:

#include <utility>

// ...

auto city_it = std::as_const(usa).find("New York");
jhasse
  • 2,379
  • 1
  • 30
  • 40
13

A clean solution is to work with a const reference to the otherwise modifiable map:

const auto &const_usa = usa;
auto city_it = const_usa.find("New York");

This will make sure you can't modify const_usa, and will use const iterators.

rustyx
  • 80,671
  • 25
  • 200
  • 267
12

This isn't a drastically different take on conversion to const in comparision to @Jollymorphic's answer, but I think that having a utility one-liner function like this is handy:

template<class T> T const& constant(T& v){ return v; }

Which makes the conversion much more appealing to the eye:

auto it = constant(usa).find("New York");
// other solutions for direct lengths comparision
std::map<std::string, int>::const_iterator city_it = usa.find("New York");
auto city_it = const_cast<const std::map<std::string, int>&>(usa).find("New York");

Well, I'd say, bigger isn't always better. You can of course choose the name of the function according to your preferences - as_const or just const_ are possible alternatives.

Xeo
  • 129,499
  • 52
  • 291
  • 397
  • I used `make_const` on the occasion I used this idea. – GManNickG Mar 05 '13 at 21:56
  • 2
    @GMan: `make_x` implies, to me atleast, the *creation* of something *new* that wasn't there before. That's why I opted for `as_const`, which changes the *view* on the object. – Xeo Mar 05 '13 at 22:51
  • 6
    c++17 has added std::as_const to the header [link](http://en.cppreference.com/w/cpp/utility/as_const) – ChetS Aug 19 '16 at 21:55
6

Another variation using auto (keeping both a mutable usa and a const usa):

map<std::string, int> usa;
//...init usa
const auto &const_usa = usa;
auto city_it = const_usa.find("New York");

If you don't need the map to be mutable at all after init there are some other options.

you can define usa as const and init it with a function call:

const map<std::string, int> usa = init_usa();
auto city_it = usa.find("New York");

or using a lambda to init a const map:

const auto usa = [&]()->const map<std::string, int> 
   {
   map<std::string, int> usa;
   //...init usa
   return usa;
   }();
auto city_it = usa.find("New York");
ChetS
  • 658
  • 7
  • 15
5

In C++11, you can do this:

decltype(usa)::const_iterator city_it = usa.find("New York");
snips-n-snails
  • 637
  • 5
  • 22
1

I'm not in a position to test this right now, but I think it'll do the trick:

auto city_it = const_cast< const map<int> & >(usa).find("New York");
Jollymorphic
  • 3,510
  • 16
  • 16
  • 1
    Hmm, but what makes this solution better than simply specifiying `map::const_iterator` as the type? It's not shorter or more readable in any way. – us2012 Mar 05 '13 at 20:17
  • 3
    @us2012: technically, OP didn't request shorter or more readable, he requested a `const_iterator` using `auto`, which is exactly what this allows. :P – Mooing Duck Mar 05 '13 at 20:22
-3

You can use auto to "track" a type or "deduce" a type: // deduce auto city_it = usa.find("New York");

// track auto city_it = std::map<int>::const_iterator( usa.find("New York"));

Also, watch is modern c++ style talks by Herb Sutter, which covers most of these type deductions guidance. https://youtu.be/xnqTKD8uD64

Sid Sarasvati
  • 819
  • 9
  • 10
  • `auto city_it = {std::map::const_iterator} usa.find("New York");` is a syntax error. Can you rephrase your answer and explain a bit more about what you are trying to say? – M.M Sep 11 '15 at 01:40
  • It is still an error, `map` is not a type. And you do not explain what the difference between "deduce" and "track" is. – M.M Sep 16 '15 at 23:27