C ++, я могу статически инициализировать std :: map во время компиляции? - PullRequest
40 голосов
/ 31 января 2010

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

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 ‘{...}’   

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

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

Но, тем не менее, мне любопытно - этов любом случае инициализировать карту, вектор и т. д. во время компиляции?


Редактировать: я должен был сказать, что я разрабатываю для встраиваемых систем.Не все процессоры будут иметь компилятор C ++ 0x.Наиболее популярным, вероятно, будет, но я не хочу сталкиваться с проблемой и должен поддерживать 2 версии кода.

Что касается Boost, я не определился.Они беззаботно относятся к использованию своих классов Finite State Machine во встроенных системах, так что это именно то, что я здесь кодирую, классы Event / State / Fsm.

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

Ответы [ 8 ]

37 голосов
/ 31 января 2010

Это не совсем статическая инициализация, но все же, попробуйте. Если ваш компилятор не поддерживает C ++ 0x, я бы выбрал std :: map в конструктор итерации :

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

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

21 голосов
/ 31 января 2010

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

Редактировать: из gcc 5 C ++ 11 включен по умолчанию

14 голосов
/ 31 января 2010

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

13 голосов
/ 31 января 2010

С C ++ 0x вам может понадобиться использовать фигурные скобки полностью (также используйте синтаксис нового стиля для каждой пары):

std::map<int, char> example = { {1,'a'}, {2, 'b'}, {3, 'c'} };

Эти скобки для построения пар не имеют смысла. В качестве альтернативы вы можете полностью назвать каждую пару или использовать make_pair (как в C ++ 98)

std::map<int, char> example = {
    std::make_pair(1,'a'),
    std::make_pair(2, 'b'),
    std::make_pair(3, 'c')
};

Что касается создания этих экземпляров во время компиляции: нет. Все контейнеры STL полностью управляют памятью во время выполнения.

Полагаю, у вас действительно будет только карта времени компиляции с библиотеками, такими как метапрограммирование boost (не уверен на 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;
4 голосов
/ 31 января 2010

При использовании pre-C ++ 0x самое близкое, что вы можете получить, это не использовать контейнеры, предназначенные для использования во время выполнения (и ограничивать себя фундаментальными типами и агрегатами) :

struct pair { int first; char second; };
pair arr[] = {{1,'a'}, {2,'b'}}; // assuming arr has static storage

Затем к этому можно обратиться, используя какое-то представление карты , или вы можете внедрить оболочку, которая допускает инициализацию агрегата, аналогично тому, что Boost.Array делает.

Конечно, вопрос заключается в том, какие выгоды оправдывают время, затраченное на его реализацию.

Если мое чтение здесь верно, списки инициализаторов C ++ 0x могут дать вам статическую инициализацию неагрегатов, таких как std::map и std::pair, но только если это не меняет семантику по сравнению с динамической инициализацией.
Таким образом, мне кажется, , вы можете получить то, о чем просили, только если ваша реализация может проверить статическим анализом, что поведение не меняется, если map статически инициализирован, но не гарантирует, что это произойдет. .

1 голос
/ 01 февраля 2010

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

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

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

1 голос
/ 31 января 2010

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

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

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

0 голосов
/ 05 сентября 2011
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 ;
...