views:

73

answers:

2

I was writing a test case out to tackle a bigger problem in my application. I ended trying some code out on codepad and discovered that some code that compiled on my local machine (g++ 4.4.1, with -Wall) didn't compile on codepad (g++ 4.1.2), even though my local machine has a newer version of g++.

Codepad calls this a reference to reference error, which I looked up and found a litle information on. It looks like it's not a good idea to have a stl container of references. Does this mean I need to define my own PairPages class? And if this is the case, why did it compile locally in the first place? What's going on?

codepad link: http://codepad.org/UAaJI1rl

#include <deque>
#include <utility>
#include <iostream>

using namespace std;

class Page {
  public:
    Page() : number_(++count) {}
    int getNum() const { return number_; }
  private:
    static int count;
    int number_;
};

int Page::count = 0;

class Book {
  public:
    Book() : currPageIdx_(3) {
      int numPages = 5;
      while (numPages > 0) {
        pages_.push_back(Page());
        numPages--; // oops
      }
    }
    pair<const Page&, const Page&> currPages() { return pagesAt(currPageIdx_); }
    pair<const Page&, const Page&> pagesAt(int pageNo) { return make_pair(pages_[pageNo - 1], pages_[pageNo]); }
    //const Page& currPages() { return pagesAt(currPageIdx_); }
    //const Page& pagesAt(int pageNo);
  private:
    deque<Page> pages_;
    int currPageIdx_;
};

int main() {
    Book book;
    cout << book.pagesAt(3).first.getNum() << endl;
    cout << book.currPages().first.getNum() << endl;
}
+3  A: 

A vector (or any STL container) of references is indeed a bad idea, as obvious when you simply look at requirements for element type T of any STL container (ISO C++03 23.1[lib.container.requirements]). It starts off by saying that "containers are objects that store other objects". We can stop right here, because a reference is not an object in C++ (unlike, say, a pointer; note that "object" in C++ parlance doesn't mean "instance of class"!). But, furthermore, it requires T to be Assignable, the requirements for which refer to type T& - if T is itself some reference type U&, then the constructed type would be U& &, which (reference to reference) is illegal in C++.

If you really want to have a container that doesn't manage lifetimes of objects, then you should use a container of pointers. If you prefer the safety of references (e.g. lack of pointer arithmetic and null value), you can use std::tr1::reference_wrapper<T> class, which is copy constructible and assignable wrapper for a reference.

Pavel Minaev
Kache4
Yes, absolutely. An object doesn't have to be created using `new` for you to be able to pass pointers around.
Pavel Minaev
A: 

On the poster's infinite loop problem: Your code loops indefinitely because in the Book constructor the counter numPages is never decreased, thus the while statement never halts until you run out of memory.

It's usually easier to spot these kind of errors by just using a for-loop:

Book() : currPageIdx_(3) {
  for(int numPages = 0; numPages < 5; ++numPages) {
    pages_.push_back(Page());
  }
}
catchmeifyoutry