Petr Skocik
Petr Skocik

Reputation: 60056

auto and distinguishing between overloaded methods

Here's my example code for some context:

//tac.cc
#include <iostream>
#include <vector>
#include <string>

using namespace std;
int main(int argc, char **argv)
{
  auto lines = vector<string>{};
  for(auto line = string{}; getline(cin,line); ){
    lines.emplace_back(line);
  };


  //vector<string>::const_reverse_iterator cit = lines.rbegin();
  //auto cit = lines.rbegin();
  for(
      decltype(lines)::const_reverse_iterator cit = lines.rbegin();
      cit != lines.rend();
      ++cit){
    cout<< *cit << endl;
  };
    return 0;
}

In the second for loop, id'd like to have the brevity of auto while getting a const_reverse_iterator out of rbegin().

According to http://www.cplusplus.com/reference/vector/vector/rbegin/ , vector's rbegin has two signatures:

 reverse_iterator rbegin() noexcept;
 const_reverse_iterator rbegin() const noexcept;

If I do use

  auto cit = lines.rbegin();

I get the non-const version of rbegin because the compiler then let's me modify the vector (e.g. *cit = "foo";) without warning.

How does auto picks from the two versions of rbegin. Can it be made to pick the second version?

Upvotes: 1

Views: 84

Answers (2)

Barry
Barry

Reputation: 302643

How does auto picks from the two versions of rbegin. Can it be made to pick the second version?

That is the wrong question. auto simply picks the type of lines.rbegin(). The question is "How does the compiler pick rbegin()?" The answer to that is we do overload resolution on the two functions using the object instance as the first argument. So we have:

reverse_iterator rbegin(vector<string>& );
const_reverse_iterator rbegin(const vector<string>& );

Both overloads are exact matches, but the latter is a reference to a more cv-qualified type (since lines is not const), so the first will be preferred. That's why you get a reverse_iterator for lines.rbegin(). auto just happily deduces it.

What you want is to force the call itself to give you a const_reverse_iterator, which requires you to "pass" a const object into rbegin. You could do that explicitly:

auto cit = const_cast<const vector<string>&>(lines).rbegin();

There's even a proposal to make this easier:

auto cit = std::as_const(lines).rbegin();

But even simpler, there's just a member function that always returns a const_reverse_iterator: crbegin

auto cit = lines.crbegin();

Upvotes: 2

Ami Tavory
Ami Tavory

Reputation: 76297

As 0x499602d2 points out, there's the cr?[begin|end]* family in C++14. In fact, your code basically appears in Scott Meyer's Effective Modern C++ (Item 13) with that as the resolution.

For the time being, you can "force" lines to be a const when you call it, like so:

  auto cit = ((const decltype(lines) &)lines).rbegin();

While there are other ways, not involving decltype, I like this one, as it is invariant to later changes in lines's type.

Upvotes: 3

Related Questions