Ініціалізація статичного std :: map <int, int> в C ++


448

Який правильний спосіб ініціалізації статичної карти? Чи потрібна нам статична функція, яка буде ініціалізувати її?

Відповіді:


619

Використання C ++ 11:

#include <map>
using namespace std;

map<int, char> m = {{1, 'a'}, {3, 'b'}, {5, 'c'}, {7, 'd'}};

Використання Boost.Assign :

#include <map>
#include "boost/assign.hpp"
using namespace std;
using namespace boost::assign;

map<int, char> m = map_list_of (1, 'a') (3, 'b') (5, 'c') (7, 'd');

115
Кожен раз, коли я бачу щось подібне, що робиться з C ++, я думаю про весь жахливий код шаблону, який повинен стояти за ним. Гарний приклад!
Грег Хьюгілл

34
Краса всього жахливого коду шаблону, який реалізує ці утиліти, полягає в тому, що він акуратно інкапсульований у бібліотеці, і кінцевому користувачеві рідко потрібно впоратися зі складністю.
Стів Ґуіді

45
@QBziZ: Якщо ваша компанія відмовиться від використання Boost на тій підставі, що вона недостатньо стандартна, мені цікаво, що бібліотека C ++ була б "достатньо стандартною". Boost - це стандартний супутник кодера C ++.
DevSolar

47
Моя проблема з Boost (і тут, і в інших місцях) полягає в тому, що часто можна обійтися без нього (в цьому випадку з C ++ 11 або раніше C ++ 11 з функцією ). Boost додає значний час компіляції, мав тонни файлів для паркування у вашому сховищі (і потрібно копіювати навколо / zip / extract, якщо ви створюєте архів). Ось чому я намагаюся не використовувати його. Я знаю, що ви можете вибирати, які файли включати / не включати, але зазвичай вам не хочеться турбуватися про перехресні залежності Boost із собою, тому ви просто копіюєте всю річ навколо.
bobobobo

7
Моя проблема Boost полягає в тому, що він часто має кілька нових залежностей бібліотеки, що, як правило, означає БОЛЬШІ пакети, які потрібно встановити, щоб правильно працювати. Нам уже потрібен libstdc ++. Наприклад, бібліотеці Boost ASIO потрібні принаймні 2 нові бібліотеки (можливо, більше), які потрібно встановити. C ++ 11/14 робить це набагато простіше, не потребуючи підвищення.
Ралі

135

Найкращим способом є використання функції:

#include <map>

using namespace std;

map<int,int> create_map()
{
  map<int,int> m;
  m[1] = 2;
  m[3] = 4;
  m[5] = 6;
  return m;
}

map<int,int> m = create_map();

18
Чому це "найкраще"? Чому, наприклад, це краще, ніж відповідь @ Dreamer?
Маркіз Лорнський

6
Я думаю, що це "найкраще", тому що це дійсно просто і не залежить від інших існуючих структур (таких як Boost :: Assign або повторне його виконання). І порівняно з відповіддю @ Dreamer, ну я уникаю створення цілої структури лише для ініціалізації карти ...
PierreBdR

3
Зауважте, тут існує небезпека . externзмінні не матимуть своїх правильних значень у цьому "перед головним конструктором часу виконання", якщо компілятор побачив лише externдекларацію, але ще не впав у фактичне визначення змінної .
бобобобо

5
Ні. Але це не проблема, пов’язана з цим питанням. Це загальна проблема зі статичними змінними.
PierreBdR

5
немає прискорення AND немає C ++ 11 => +1. Зауважте, що цю функцію можна використовувати для ініціалізації const map<int,int> m = create_map()(і так, ініціалізації членів класу класу зі списку ініціалізації:struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
ribamar

115

Не складне питання зробити щось подібне до підвищення. Ось клас із трьома функціями, включаючи конструктор, для реплікації того, що було (майже).

template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(const T& key, const U& val)
    {
        m_map[key] = val;
    }

    create_map<T, U>& operator()(const T& key, const U& val)
    {
        m_map[key] = val;
        return *this;
    }

    operator std::map<T, U>()
    {
        return m_map;
    }
};

Використання:

std :: map mymap = create_map <int, int> (1,2) (3,4) (5,6);

Вищевказаний код найкраще працює для ініціалізації глобальних змінних або статичних членів класу, який потрібно ініціалізувати, і ви не маєте уявлення про те, коли він спочатку використовується, але ви хочете переконатися, що значення в ньому є.

Якщо ви скажете, вам потрібно вставити елементи в існуючий std :: map ... ось ще один клас для вас.

template <typename MapType>
class map_add_values {
private:
    MapType mMap;
public:
    typedef typename MapType::key_type KeyType;
    typedef typename MapType::mapped_type MappedType;

    map_add_values(const KeyType& key, const MappedType& val)
    {
        mMap[key] = val;
    }

    map_add_values& operator()(const KeyType& key, const MappedType& val) {
        mMap[key] = val;
        return *this;
    }

    void to (MapType& map) {
        map.insert(mMap.begin(), mMap.end());
    }
};

Використання:

typedef std::map<int, int> Int2IntMap;
Int2IntMap testMap;
map_add_values<Int2IntMap>(1,2)(3,4)(5,6).to(testMap);

Дивіться це в дії з GCC 4.7.2 тут: http://ideone.com/3uYJiH

############### ВСІШЕ НІСЬКОГО, ЩО ЦЕ ОБОВ'ЯЗАНО #################

EDIT : map_add_valuesКлас нижче, який був оригінальним рішенням, який я запропонував, не вдасться, якщо мова йде про GCC 4.5+. Будь ласка, подивіться на код вище, як додати значення до існуючої карти.


template<typename T, typename U>
class map_add_values
{
private:
    std::map<T,U>& m_map;
public:
    map_add_values(std::map<T, U>& _map):m_map(_map){}
    map_add_values& operator()(const T& _key, const U& _val)
    {
        m_map[key] = val;
        return *this;
    }
};

Використання:

std :: map <int, int> my_map;
// Пізніше десь уздовж коду
map_add_values ​​<int, int> (my_map) (1,2) (3,4) (5,6);

ПРИМІТКА. Раніше я використовував а operator []для додавання фактичних значень. Це неможливо, як коментує dalle.

###################### КІНЦЯ ОБОВ'ЯЗАНОГО РОЗДІЛУ ######################


3
Я використовую ваш перший зразок як <int, string> для зв’язування номерів помилок (від перерахунків) із повідомленнями - це працює як шарм - дякую.
slashmais

1
operator[]бере лише один аргумент.
Далле

1
@dalle: Гарний улов! Я чомусь думав, що перевантажені [] оператори можуть прийняти більше.
Віте Сокіл

2
Це фантастична відповідь. Соромно, що ОП жодного разу не обрав. Ви заслуговуєте мега-реквізиту.
Thomas Thorogood

map_add_values ​​не працює в gcc, який скаржиться: error: conflicting declaration ‘map_add_values<int, int> my_map’ error: ‘my_map’ has a previous declaration as ‘std::map<int, int> my_map’
Martin Wang

42

Ось ще один спосіб, який використовує 2-елементний конструктор даних. Для його ініціалізації не потрібні функції. Немає стороннього коду (Boost), немає статичних функцій або об'єктів, немає хитрощів, просто простий C ++:

#include <map>
#include <string>

typedef std::map<std::string, int> MyMap;

const MyMap::value_type rawData[] = {
   MyMap::value_type("hello", 42),
   MyMap::value_type("world", 88),
};
const int numElems = sizeof rawData / sizeof rawData[0];
MyMap myMap(rawData, rawData + numElems);

Оскільки я написав цю відповідь, C ++ 11 не працює. Тепер ви можете безпосередньо ініціалізувати контейнери STL за допомогою нової функції списку ініціалізаторів:

const MyMap myMap = { {"hello", 42}, {"world", 88} };

25

Наприклад:

const std::map<LogLevel, const char*> g_log_levels_dsc =
{
    { LogLevel::Disabled, "[---]" },
    { LogLevel::Info,     "[inf]" },
    { LogLevel::Warning,  "[wrn]" },
    { LogLevel::Error,    "[err]" },
    { LogLevel::Debug,    "[dbg]" }
};

Якщо карта є членом даних класу, ви можете ініціалізувати його безпосередньо в заголовку наступним чином (оскільки C ++ 17):

// Example

template<>
class StringConverter<CacheMode> final
{
public:
    static auto convert(CacheMode mode) -> const std::string&
    {
        // validate...
        return s_modes.at(mode);
    }

private:
    static inline const std::map<CacheMode, std::string> s_modes =
        {
            { CacheMode::All, "All" },
            { CacheMode::Selective, "Selective" },
            { CacheMode::None, "None" }
            // etc
        };
}; 

24

Я б загорнув карту всередину статичного об'єкта і поклав би код ініціалізації карти в конструктор цього об'єкта, таким чином ви впевнені, що карта створена до виконання коду ініціалізації.


1
Я з вами на цьому. Це також
тад

2
Тад швидше за що? Глобальна статика з ініціалізатором? Ні, це не так (пам’ятайте про RVO).
Павло Мінаєв

7
Гарна відповідь. Буду радий, якщо побачу фактичний приклад коду
Sungguk Lim

18

Просто хотів поділитися чистою роботою на C ++ 98:

#include <map>

std::map<std::string, std::string> aka;

struct akaInit
{
    akaInit()
    {
        aka[ "George" ] = "John";
        aka[ "Joe" ] = "Al";
        aka[ "Phil" ] = "Sue";
        aka[ "Smitty" ] = "Yando";
    }
} AkaInit;

2
це не працює для об’єкта без конструктора за замовчуванням, слід віддавати перевагу методу вставки IMHO
Alessandro Teruzzi

16

Ви можете спробувати:

std::map <int, int> mymap = 
{
        std::pair <int, int> (1, 1),
        std::pair <int, int> (2, 2),
        std::pair <int, int> (2, 2)
};

1
Ви не можете використовувати списки ініціалізаторів із типами без сукупності перед C ++ 11, і в цьому випадку ви можете також використовувати коротший синтаксис {1, 2}замість std::pair<int, int>(1, 2).
Ферруччо

9

Це схоже на PierreBdRбез копіювання карти.

#include <map>

using namespace std;

bool create_map(map<int,int> &m)
{
  m[1] = 2;
  m[3] = 4;
  m[5] = 6;
  return true;
}

static map<int,int> m;
static bool _dummy = create_map (m);

12
Це, мабуть, не було б скопійовано.
GManNickG

2
але ця карта не може стати статичною, чи не так?
xmoex

6

Якщо ви застрягли в C ++ 98 і не хочете використовувати boost, ось яке рішення я використовую, коли мені потрібно ініціалізувати статичну карту:

typedef std::pair< int, char > elemPair_t;
elemPair_t elemPairs[] = 
{
    elemPair_t( 1, 'a'), 
    elemPair_t( 3, 'b' ), 
    elemPair_t( 5, 'c' ), 
    elemPair_t( 7, 'd' )
};

const std::map< int, char > myMap( &elemPairs[ 0 ], &elemPairs[ sizeof( elemPairs ) / sizeof( elemPairs[ 0 ] ) ] );

-4

У вас тут є дуже хороші відповіді, але я мені, це схоже на випадок "коли все, що ти знаєш, - молот" ...

Найпростіша відповідь на те, чому не існує стандартного способу ініціалізації статичної карти, чи немає вагомих причин коли-небудь використовувати статичну карту ...

Карта - це структура, призначена для швидкого пошуку невідомого набору елементів. Якщо ви знаєте елементи раніше, просто використовуйте C-масив. Введіть значення впорядкованому порядку або виконайте сортування за ними, якщо ви не можете цього зробити. Потім ви можете отримати продуктивність журналу (n), використовуючи функції stl :: для циклічного запису, нижній_бійний / верхній_обмежений. Коли я тестував це раніше, вони зазвичай працюють принаймні в 4 рази швидше, ніж карта.

Переваги в багато разів ... - швидша продуктивність (* 4, я вимірював багато типів процесора, це завжди близько 4) - простіша налагодження. Просто простіше зрозуміти, що відбувається з лінійним компонуванням. - Тривіальні реалізації операцій копіювання, якщо це стане необхідним. - Він не виділяє пам'яті під час виконання, тому ніколи не кине виняток. - Це стандартний інтерфейс, і тому дуже легко ділитися на DLL-файлах чи на мовах тощо.

Я міг би продовжити, але якщо ви хочете більше, чому б не переглянути багато блог Stroustrup на цю тему.


8
Продуктивність - не єдина причина використання карти. Наприклад, існує багато випадків, коли ви хочете з'єднати значення разом (наприклад, код помилки із повідомленням про помилку), а карта робить використання та доступ порівняно простим. Але посилання на ці записи в блозі може бути цікавим, можливо, я щось роблю не так.
MatthiasB

5
Масив набагато простіше і має більш високу продуктивність, якщо ви можете ним користуватися. Але якщо індекси (ключі) не є суміжними та широко розставленими, вам потрібна карта.
КарлУ

1
A mapтакож є корисною формою для представлення часткової функції (функція в математичному сенсі; але також, вид, у сенсі програмування). Масив цього не робить. Не можна, скажімо, шукати дані з масиву за допомогою рядка.
einpoklum

3
Ваша відповідь не намагається відповісти на дійсне запитання, а замість цього роздумує над обмеженнями мови, пропонує рішення різних проблем, отже, зворотне голосування. Реальний сценарій - відображення (безперервне чи ні) коди помилок бібліотеки на текстові рядки. З масивом час пошуку - O (n), який можна покращити статичним відображенням до O (log (n)).
Тоша

2
Якщо справді "немає жодної вагомої причини коли-небудь використовувати статичну карту ...", то дуже дивно, що в C ++ 11 додано синтаксис (списки ініціалізаторів), які спрощують їх використання.
ellisbben
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.