Dowemo
0 0 0 0


Question:

I want to define a global container (C++03), and here's an example code I tried, which does not work.

#include <vector>
#include <string>
using namespace std;
vector<string> Aries;
Aries.push_back("Taurus");    // line 6
int main() {}

Compile error:

prog.cpp:6:1: error: 'Aries' does not name a type

It seems I can define an empty global vector, but cannot fill it up. Looks like in C++03, I cannot specify an initializer either, such as:

vector<string> Aries = { "Taurus" };

Have I made a mistake here, or how do I get around this problem?

I tried searching on StackOverflow to see if this has been answered before, but only came across these posts: global objects in C++, Defining global constant in C++, which did not help answer this.


Best Answer:


While declarations and initializations outside of a function (such as main) are no problems, you cannot have code outside of functions. You either need to set the value right at the initialization (as in C++11), or fill your global object in main:

std::vector<string> Aries;
/* ... */
int main() {
    Aries.push_back("Taurus");
    /* ... */
}

Other ways (without populating the vector in main)

Single values

There are other ways which don't need .push_back or other code in the main. For example, if Aries shall contain only one item, you can use std::vector<T>::vector(size_t s, T t) with s == 1 and t == "Taurus":

std::vector<string> Aries(1, "Taurus");
/* ... */
int main() { /* ... */ }

If you need the same value several time you can simply adjust s.

Multiple distinct value

Now this gets a little bit trickier. You want to populate the vector by using a suitable constructor. std::vector<T> provides four different constructors in C++03:

  • std::vector<T>::vector()
  • std::vector<T>::vector(size_t, T = T())
  • template <class InputIt> std::vector<T>::vector(InputIt, InputIt)
  • std::vector<T>::vector(const vector&)Note that all of them actually take an allocator as additional last parameter, but this is not of our concern.
  • We can forget about std::vector<T>::vector(), since we want to fill the vector with values, and also std::vector<T>::vector(size_t, T) doesn't fit, since we want distinct values. What's left is the templated constructor and the copy constructor. The following example shows how to use the templated constructor:

    std::string const values[] = {"Taurus", "Ares", "Testos"};
    template <class T, size_t N>
    T* begin(T (&array)[N]){ // this is already in C++11, very helpful 
        return array;
    }
    template <class T, size_t N>
    T* end(T (&array)[N]){
        return array+N;
    }
    std::vector<std::string> Aries(begin(values), end(values));

    Note that begin and end aren't necessary - we could simple use Aries(values, values+3). However, things tend to change, and often you add a value or remove one. If you forget to change the offset 3 you would either forget an entry or cross borders of values.

    However, this solution introduces a new global variable, which aren't that nice. Lets take a step back. We needed values since we wanted to use std::vector<T>::vector(InputIt, InputIt), which needs a valid range. And the range must be known at the time we use the constructor, so it needs to be global. Curses! But behold: our toolbox still contains one constructor, the copy constructor. In order to use it we create an additional function:

    namespace {
        std::vector<std::string> initializer(){
            const std::string dummy_array[] = {"Taurus", "Ares", "Testos"};
            return std::vector<std::string>(begin(dummy_array), end(dummy_array));
        }
    }
    std::vector<std::string> Aries(initializer());

    This example uses both copy constructor and range constructor. You could also create a temporary vector and use std::vector<T>::push_back to populate it in the function.




    Copyright © 2011 Dowemo All rights reserved.    Creative Commons   AboutUs