0
struct C{
    std::vector<int> foo;
    const int &bar;
    C();
};

C::C(): bar(foo[0]){
    foo.push_back(5);
}

The code could compile but the compiled file could not run.

What is wrong with this way of initialising the reference bar? I thought that as long as foo is declared before bar, we could use foo[0] to initialise bar in the constructor?

Can anyone give any ideas as why this might be the case?

user22119
  • 717
  • 1
  • 4
  • 18
  • 2
    This is very dangerous, even if you could make that particular construction work: Vector element references are generally *invalidated* when you mutate the vector. Why not use a non-resizable container? (Or at least a `deque`, which doesn't invalidate references quite as easily.) – Kerrek SB Jul 18 '13 at 23:06
  • If I were to use a non-resizeable container, won't the problem still exist since I would be essentially initializing bar first (where foo could be a deque). – user22119 Jul 18 '13 at 23:17
  • Yes, of course, you have to initialize the container first, but that's trivial: `C::C() : foo(100), bar(foo.front()) { }`, or in C++11, `C::C() : foo { 1, 2, 3, 4 }, bar { foo.front() } { }`. – Kerrek SB Jul 18 '13 at 23:30

2 Answers2

0

Your vector is in empty state. Initialization lists constructs the object before the code in the brackets, so your push_back doesn't affect bar(foo[0]). So your foo[0] is accessing an empty vector and is out of bounds.

Note that references to vector elements get invalidated whenever resizing happens. So it wouldn't be worth it to keep a reference to a vector element.

Rapptz
  • 20,807
  • 5
  • 72
  • 86
0

since your foo is uninitialized you can initialize your foo with a size at least 1 before bar by using the initializer list. this is also c++98 valid.

struct C{
    std::vector<int> foo;
    const int &bar;
    C();
};

C::C() : foo(1)  // initialize foo with size 1
       , bar(foo[0]) {
    foo[0]=5;    // this is guarateed to be exception safe
}

but you have to pay attention to the initializer list order. it is ordered by the order of declaration in the struct/class. see here

According to ISO/IEC 14882:2003(E) section 12.6.2:

Initialization shall proceed in the following order:

  • First, and only for the constructor of the most derived class as described below, virtual base classes shall be initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base class names in the derived class base-specifier-list.
  • Then, direct base classes shall be initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
  • Then, nonstatic data members shall be initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
  • Finally, the body of the constructor is executed.

edit:

thanks to Kerrek SB's comment, you can't use your vector. while you push something foo will be resized (reallocated) and bar will lose the right reference. you could either use a std::deque or init the vector to a specific size that never will be passed.

Community
  • 1
  • 1
user1810087
  • 5,146
  • 1
  • 41
  • 76
  • Thanks, your answer addressed my problem exactly. My problem was that I did not know how to initialise foo first. – user22119 Jul 18 '13 at 23:19
  • @user22119 This would reserve 1 element. Then it'd use the push_back and it'll take that one reserved spot. After that the element will resize and there will be a chance that the reference will be invalidated. – Rapptz Jul 18 '13 at 23:22
  • EVen though this is syntactically correct, it's still UB, since `push_back` invalidates references (absent a suitable `capacity()` check, which you aren't doing). – Kerrek SB Jul 18 '13 at 23:23
  • @KerrekSB now it should be safe an run as expected. – user1810087 Jul 18 '13 at 23:27
  • @itwasntpete: Yes, but it is still completely pointless, since you can never ever ever add anything else to the vector... – Kerrek SB Jul 18 '13 at 23:29