Ответ 1
Не в С++ 98. С++ 11 поддерживает это, поэтому, если вы включаете флаги С++ 11 и включаете то, что предлагает g++, вы можете.
Изменить: из gcc 5 С++ 11 включен по умолчанию
Если я закодирую этот
std::map<int, char> example = {
(1, 'a'),
(2, 'b'),
(3, 'c')
};
тогда g++ говорит мне
deducing from brace-enclosed initializer list requires #include <initializer_list>
in C++98 ‘example’ must be initialized by constructor, not by ‘{...}’
и это немного раздражает меня, потому что конструктор является временем выполнения и может теоретически терпеть неудачу.
Конечно, если это произойдет, он быстро сработает и должен делать это последовательно, так что я должен быстро найти и исправить проблему.
Но, тем не менее, мне любопытно - есть ли способ инициализировать карту, вектор и т.д. во время компиляции?
Изменить: я должен был сказать, что я разрабатываю для встроенных систем. Не все процессоры будут иметь компилятор С++ 0x. Самый популярный, вероятно, будет, но я не хочу встречаться с gotcha и должен поддерживать 2 версии кода.
Что касается Boost, я не знаю. Они желательны в использовании своих классов конечных машин в встроенных системах, поэтому на самом деле я кодирую здесь классы Event/State/Fsm.
Вздох, я думаю, мне лучше играть в это безопасно, но я надеюсь, что эта дискуссия была полезной для других.
Не в С++ 98. С++ 11 поддерживает это, поэтому, если вы включаете флаги С++ 11 и включаете то, что предлагает g++, вы можете.
Изменить: из gcc 5 С++ 11 включен по умолчанию
Это не совсем статическая инициализация, но все же попробуйте. Если ваш компилятор не поддерживает С++ 0x, я бы пошел на конструктор std:: map iteration:
std::pair<int, std::string> map_data[] = {
std::make_pair(1, "a"),
std::make_pair(2, "b"),
std::make_pair(3, "c")
};
std::map<int, std::string> my_map(map_data,
map_data + sizeof map_data / sizeof map_data[0]);
Это довольно читаемо, не требует дополнительных библиотек и должно работать во всех компиляторах.
Вы можете использовать Boost.Assign:
#include <boost/assign.hpp>
#include <map>
int main()
{
std::map<int, char> example =
boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c');
}
Однако, как указал Нил и другие в комментариях ниже, эта инициализация происходит во время выполнения, аналогично предложению UncleBean.
С С++ 0x вам может потребоваться полностью использовать фигурные скобки (используйте синтаксис нового стиля для каждой пары):
std::map<int, char> example = { {1,'a'}, {2, 'b'}, {3, 'c'} };
Эти скобки для построения пар не имеют смысла. В качестве альтернативы вы можете полностью определить каждую пару или использовать make_pair (как и в С++ 98)
std::map<int, char> example = {
std::make_pair(1,'a'),
std::make_pair(2, 'b'),
std::make_pair(3, 'c')
};
Что касается создания этих экземпляров во время компиляции: нет. Контейнеры STL все инкапсулируют полностью управление памятью во время выполнения.
Я предполагаю, что на самом деле у вас будет только карта времени компиляции с такими библиотеками, как метапрограммирование ускорения (не уверенно на 100%, если она полностью правильная и не изучила, для чего это может быть полезно):
using namespace boost::mpl;
map<
pair<integral_c<int, 1>, integral_c<char, 'a'> >,
pair<integral_c<int, 2>, integral_c<char, 'b'> >,
pair<integral_c<int, 3>, integral_c<char, 'c'> >
> compile_time_map;
С pre-С++ 0x самое близкое, что вы можете получить, - это не использование контейнеров, предназначенных для использования во время работы (и ограничение самих основных типов и агрегатов):
struct pair { int first; char second; };
pair arr[] = {{1,'a'}, {2,'b'}}; // assuming arr has static storage
После этого можно было бы получить доступ с помощью какого-либо вида карты, или вы могли бы реализовать оболочку, которая позволяет выполнить агрегатную инициализацию, похожую на то, что Boost.Array делает.
Конечно, вопрос заключается в том, что преимущества оправдывают время, затраченное на реализацию этого.
Если мое чтение верное здесь, списки инициализаторов С++ 0x могут дать вам статическую инициализацию неагрегатов, таких как std::map
и std::pair
, но только если это не изменяет семантики по сравнению с динамической инициализацией. < ш > Таким образом, мне кажется, вы можете получить только то, что вы просили, если ваша реализация может проверить с помощью статического анализа, что поведение не меняется, если map
статически инициализируется, но не гарантирует, что это произойдет.
Есть трюк, который вы можете использовать, но только если эти данные не будут использоваться ни в каком другом статическом конструкторе. Сначала определите простой класс следующим образом:
typedef void (*VoidFunc)();
class Initializer
{
public:
Initializer(const VoidFunc& pF)
{
pF();
}
};
Затем используйте его следующим образом:
std::map<std::string, int> numbers;
void __initNumsFunc()
{
numbers["one"] = 1;
numbers["two"] = 2;
numbers["three"] = 3;
}
Initializer __initNums(&__initNumsFunc);
Конечно, это немного перебор, поэтому я бы рекомендовал использовать его, только если вам действительно нужно.
Нет стандартного способа инициализации std::map
во время компиляции. Как отмечали другие, С++ 0x позволит компилятору оптимизировать инициализацию как можно более статичную, но это никогда не будет гарантировано.
Помните, однако, что STL - это просто спецификация интерфейса. Вы можете создавать свои собственные совместимые контейнеры и предоставлять им возможность статической инициализации.
В зависимости от того, планируете ли вы модернизировать свой компилятор и реализацию STL (особенно на встроенной платформе), вы даже можете вникнуть в используемую вами реализацию, добавить производные классы и использовать их!
template <const int N> struct Map { enum { value = N}; };
template <> struct Map <1> { enum { value = (int)'a'}; };
template <> struct Map <2> { enum { value = (int)'b'}; };
template <> struct Map <3> { enum { value = (int)'c'}; };
std::cout << Map<1>::value ;