Reputation: 11152
I did some C++ assessment questions and stumbled upon this tricky program.
#include <deque>
#include <iostream>
using namespace std;
template<typename T>
ostream & print(T &start, T &end)
{
for(; start != end; ++start)
{
cout<< *start<< " ";
}
return cout;
}
int main()
{
int tab[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
deque<int> d1(tab, tab+10);
deque<int> d2;
deque<int>::iterator it;
for(it = d1.begin(); it != d1.end(); ++it)
{
d2.push_back(d1[d1.end()-it-1]); //LINE I
}
print(d2.rbegin(), d2.rend()) << endl; //LINE II
return 0;
}
I chose the option program will run successfully and display: 1 2 3 4 5 6 7 8 9 10
I later compiled the program to test it and it does not compile with the error message:
$g++ -o main *.cpp main.cpp: In function ‘int main()’: main.cpp:25:17: error: cannot bind non-const lvalue reference of type ‘std::reverse_iterator<std::_Deque_iterator<int, int&, int*> >&’ to an rvalue of type ‘std::deque<int>::reverse_iterator {aka std::reverse_iterator<std::_Deque_iterator<int, int&, int*> >}’ print(d2.rbegin(), d2.rend()) << endl; //LINE II
~~~~~~~~~^~ main.cpp:6:32: note: initializing argument 1 of ‘std::ostream& print(T&, T&) [with T = std::reverse_iterator<std::_Deque_iterator<int, int&, int*> >; std::ostream = std::basic_ostream<char>]’ template<typename T> ostream & print(T &start, T &end)
^~~~~
This error message was actually one of the options, but I did not think it would not compile.
I do not really understand what the issue is. I figured if I change the parameters of the print
function as below then it compiles and runs successfully:
template<typename T> ostream & print(T start, T end)
{
for(; start != end; ++start)
{
cout<< *start<< " ";
}
return cout;
}
Why is that? How to understand the error message if the parameters of the print
function were references ?
Upvotes: 0
Views: 33
Reputation: 10740
The parameters of the print function are references, but a const reference is still a reference. If I have an expression that returns by value (like d2.begin()
), then it can only bind to a const reference, so your print function gets called with const references.
Because print
takes iterators, you should just pass them by value. Iterators are trivial to copy (and oftentimes the compiler can pass them directly in the CPU’s registers, so no copy is even necessary). We can rewrite print
just by removing the references:
template<typename T>
ostream & print(T start, T end)
{
for(; start != end; ++start)
{
cout<< *start<< " ";
}
return cout;
}
Upvotes: 0
Reputation: 20918
Lvalue reference can be bound to Lvalues. If print
takes its arguments by Lvalue reference your code can compile when you create Lvalues from rbegin/rend
:
auto it1 = d2.rbegin();
auto it2 = d2.rend();
print(it1,it2); // pass Lvalues
When you call print(d2.rbegin(),d2.rend())
, rbegin/rend
returns iterator by value, so these iterators are temporary objects, and because binding temporary object (Rvalue) to Lvalue reference is illegal, your code doesn't compile.
Iterators are lightweight objects, you don't need to pass them by reference, just copy them.
Upvotes: 1