Ely
Ely

Reputation: 11152

C++ function with reference parameters errors out for iterator. Seeking explanation

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

Answers (2)

Alecto
Alecto

Reputation: 10740

Explanation if the compiler error

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.

The fix

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

rafix07
rafix07

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

Related Questions