Reputation: 60056
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
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
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