С++, могу ли я статически инициализировать std:: map во время компиляции?

Если я закодирую этот

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.

Вздох, я думаю, мне лучше играть в это безопасно, но я надеюсь, что эта дискуссия была полезной для других.

Ответы

Ответ 1

Не в С++ 98. С++ 11 поддерживает это, поэтому, если вы включаете флаги С++ 11 и включаете то, что предлагает g++, вы можете.

Изменить: из gcc 5 С++ 11 включен по умолчанию

Ответ 2

Это не совсем статическая инициализация, но все же попробуйте. Если ваш компилятор не поддерживает С++ 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]);

Это довольно читаемо, не требует дополнительных библиотек и должно работать во всех компиляторах.

Ответ 3

Вы можете использовать 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.

Ответ 4

С С++ 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;

Ответ 5

С 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 статически инициализируется, но не гарантирует, что это произойдет.

Ответ 6

Есть трюк, который вы можете использовать, но только если эти данные не будут использоваться ни в каком другом статическом конструкторе. Сначала определите простой класс следующим образом:

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);


Конечно, это немного перебор, поэтому я бы рекомендовал использовать его, только если вам действительно нужно.

Ответ 7

Нет стандартного способа инициализации std::map во время компиляции. Как отмечали другие, С++ 0x позволит компилятору оптимизировать инициализацию как можно более статичную, но это никогда не будет гарантировано.

Помните, однако, что STL - это просто спецификация интерфейса. Вы можете создавать свои собственные совместимые контейнеры и предоставлять им возможность статической инициализации.

В зависимости от того, планируете ли вы модернизировать свой компилятор и реализацию STL (особенно на встроенной платформе), вы даже можете вникнуть в используемую вами реализацию, добавить производные классы и использовать их!

Ответ 8

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 ;