How to setup a global container (C++03)?

痴心易碎 提交于 2019-12-21 05:45:07

问题


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.


回答1:


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:

  1. std::vector<T>::vector()
  2. std::vector<T>::vector(size_t, T = T())
  3. template <class InputIt> std::vector<T>::vector(InputIt, InputIt)
  4. 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.




回答2:


I found a neat workaround to "initialize" C++03 global STL containers (and indeed to execute code "globally" before main()). This uses the comma operator. See example:

#include <vector>
#include <string>
#include <iostream>
using namespace std;

vector<string> Aries;

// dummy variable initialization to setup the vector.
// using comma operator here to cause code execution in global scope.
int dummy = (Aries.push_back("Taurus"), Aries.push_back("Leo"), 0);

int main() {
    cout << Aries.at(0) << endl;
    cout << Aries.at(1) << endl;
}

Output

Taurus
Leo

The only real problem, if you can call it that, is the extra global variable.




回答3:


My experience is that this "amazing, yet terrifying" (hat tip) solution is unpredictable. Your initialization runs at load time. You have no guarantee as to load ORDER. So everything that uses that container must be either in the same module, or somehow guarantee that the module is loaded before they access the container. Otherwise, the container constructor has not run, yet your code happily calls its accessors.

When it goes wrong, it goes very wrong, and the error is compiler, platform, and phase of the moon dependent - in all cases giving not the remotest clue as to what went south.




回答4:


And just for completeness, this solution uses iterator pair constructor and plain old array initializer.

#include <vector>
#include <string>
#include <iostream>
using namespace std;

string contents[] = {"Taurus", "Leo"};
vector<string> Aries(contents, contents + sizeof contents/sizeof contents[0]);

int main() {
    cout << Aries.at(0) << endl;
    cout << Aries.at(1) << endl;
}

This may be clearer, yet it calls more copy constructors.



来源:https://stackoverflow.com/questions/13899866/how-to-setup-a-global-container-c03

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!