Я хочу определить глобальный контейнер (C ++ 03), и вот пример кода, который я пробовал, который не работает.
#include <vector>
#include <string>
using namespace std;
vector<string> Aries;
Aries.push_back("Taurus"); // line 6
int main() {}
Ошибка компиляции:
prog.cpp:6:1: error: 'Aries' does not name a type
Кажется, я могу определить пустой глобальный вектор, но не могу его заполнить. Похоже, в C ++ 03, я не могу также указать инициализатор, например:
vector<string> Aries = { "Taurus" };
Я ошибся здесь или как обойти эту проблему?
Я попытался выполнить поиск в StackOverflow, чтобы узнать, получен ли ответ на этот вопрос ранее, но натолкнулся только на следующие сообщения: глобальные объекты в C ++, Определение глобальной константы в C ++, что не помогло ответить на это.
В то время как объявления и инициализации вне функции (такие как main
) нет проблем, вы не можете иметь код вне функций. Вам нужно либо установить значение прямо при инициализации (как в C ++ 11), либо заполнить глобальный объект в main:
std::vector<string> Aries;
/* ... */
int main() {
Aries.push_back("Taurus");
/* ... */
}
Есть и другие способы, которые не нужны .push_back
или другой код в основном. Например, если Aries
должен содержать только один элемент, вы можете использовать std::vector<T>::vector(size_t s, T t)
с s == 1
а также t == "Taurus"
:
std::vector<string> Aries(1, "Taurus");
/* ... */
int main() { /* ... */ }
Если вам нужно одно и то же значение несколько раз, вы можете просто настроить s
,
Теперь это становится немного сложнее. Вы хотите заполнить vector
используя подходящий конструктор. std::vector<T>
предоставляет четыре разных конструктора в 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&)
Мы можем забыть о std::vector<T>::vector()
, поскольку мы хотим заполнить вектор значениями, а также std::vector<T>::vector(size_t, T)
не подходит, так как мы хотим отчетливый ценности. То, что осталось, это шаблонный конструктор и конструктор копирования. В следующем примере показано, как использовать шаблонный конструктор:
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));
Обратите внимание, что begin
а также end
не нужны — мы могли бы просто использовать Aries(values, values+3)
, Однако вещи имеют тенденцию меняться, и часто вы добавляете значение или удаляете его. Если вы забыли изменить смещение 3
вы либо забудете въезд, либо пересечете границы values
,
Однако, это решение вводит новую глобальную переменную, которая не так хороша. Давайте сделаем шаг назад. Нам нужно values
так как мы хотели использовать std::vector<T>::vector(InputIt, InputIt)
, который нуждается в действительном диапазоне. И диапазон должен быть известен в то время, когда мы используем конструктор, поэтому он должен быть глобальным. Проклятия! Но вот: наш инструментарий все еще содержит один конструктор, конструктор копирования. Чтобы использовать его, мы создаем дополнительную функцию:
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());
В этом примере используется как конструктор копирования, так и конструктор диапазона. Вы также можете создать временный вектор и использовать std::vector<T>::push_back
заполнить его в функции.
Я нашел изящный обходной путь для «инициализации» глобальных контейнеров STL C ++ 03 (и, действительно, для выполнения кода «глобально» раньше main()
). Это использует оператор запятой. Смотрите пример:
#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;
}
Выход
Taurus
Leo
Единственная реальная проблема, если вы можете это так назвать, — это дополнительная глобальная переменная.
По моему опыту, это «удивительное, но ужасающее» решение (шляпный наконечник) непредсказуемо. Ваша инициализация выполняется во время загрузки. У вас нет гарантии как загрузить ЗАКАЗ. Таким образом, все, что использует этот контейнер, должно быть либо в том же модуле, либо каким-то образом гарантировать, что модуль будет загружен до того, как они получат доступ к контейнеру. В противном случае конструктор контейнера не запустится, но ваш код с радостью вызывает его методы доступа.
Когда он идет не так, он идет очень плохо, и ошибка зависит от компилятора, платформы и фазы луны — во всех случаях не дает ни малейшего понятия о том, что пошло на юг.
И просто для полноты, это решение использует конструктор пары итераторов и простой старый инициализатор массива.
#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;
}
Это может быть понятнее, но все же вызывает больше конструкторов копирования.