Inicjowanie statycznego std :: map <int, int> w C ++

448

Jaki jest właściwy sposób inicjowania mapy statycznej? Czy potrzebujemy funkcji statycznej, która ją zainicjuje?

Nithin
źródło

Odpowiedzi:

619

Za pomocą C ++ 11:

#include <map>
using namespace std;

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

Za pomocą 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');
Ferruccio
źródło
115
Za każdym razem, gdy widzę coś takiego zrobionego w C ++, myślę o całym przerażającym kodzie szablonu, który musi za tym stać. Dobry przykład!
Greg Hewgill
34
Piękno całego przerażającego kodu szablonu, który implementuje te narzędzia, polega na tym, że jest on starannie zamknięty w bibliotece, a użytkownik końcowy rzadko musi radzić sobie ze złożonością.
Steve Guidi
45
@QBziZ: Jeśli Twoja firma odmówi użycia Boosta z tego powodu, że nie jest „wystarczająco standardowa”, zastanawiam się, która biblioteka C ++ byłaby „wystarczająco standardowa”. Boost jest standardowy towarzysz dla C ++ kodera.
DevSolar
47
Mój problem z Boostem (tutaj i gdzie indziej) polega na tym, że często możesz sobie bez niego poradzić (w tym przypadku z C ++ 11 lub przed C ++ 11 z funkcją ). Boost dodaje znaczny narzut czas kompilacji, miał mnóstwo plików do zaparkowania w twoim repozytorium (i do kopiowania / zip / rozpakowywania, jeśli tworzysz archiwum). Dlatego staram się go nie używać. Wiem, że możesz wybrać, które pliki mają zostać uwzględnione / nieuwzględnione, ale zwykle nie chcesz się martwić o wzajemne zależności Boosta, więc po prostu skopiuj wszystko.
bobobobo
7
Mój problem z Boost polega na tym, że często ma kilka nowych zależności bibliotek, co ogólnie oznacza WIĘCEJ pakietów, które muszą zostać zainstalowane, aby działać poprawnie. Potrzebujemy już libstdc ++. Na przykład biblioteka Boost ASIO wymaga co najmniej 2 nowych bibliotek (prawdopodobnie więcej), które należy zainstalować. C ++ 11/14 sprawia, że ​​dużo łatwiej nie potrzebuje wzmocnienia.
Rahly,
135

Najlepszym sposobem jest użycie funkcji:

#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();
PierreBdR
źródło
18
Dlaczego to jest „najlepsze”? Dlaczego na przykład jest lepsza niż odpowiedź @ Dreamer?
Markiz Lorne
6
Myślę, że jest „najlepszy”, ponieważ jest naprawdę prosty i nie zależy od innych istniejących struktur (takich jak Boost :: Assign lub jego reimplementacja). I w porównaniu z odpowiedzią @ Dreamera, cóż,
unikam
3
Uwaga: istnieje niebezpieczeństwo . externzmienne nie będą miały poprawnych wartości w tym „przed głównym konstruktorem czasu wykonywania”, jeśli kompilator zobaczył tylko externdeklarację, ale nie natrafił jeszcze na rzeczywistą definicję zmiennej .
bobobobo
5
Nie, niebezpieczeństwo polega na tym, że nic nie mówi, w jakiej kolejności należy inicjalizować zmienne statyczne (przynajmniej między jednostkami kompilacji). Ale nie jest to problem związany z tym pytaniem. Jest to ogólny problem ze zmiennymi statycznymi.
PierreBdR
5
bez wzmocnienia ORAZ brak C ++ 11 => +1. Zauważ, że funkcja może być użyta do zainicjowania const map<int,int> m = create_map()(a więc, zainicjowania stałych elementów klasy na liście inicjalizacji:struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
ribamar,
115

Stworzenie czegoś podobnego do ulepszenia nie jest skomplikowane. Oto klasa z trzema funkcjami, w tym konstruktorem, do replikacji tego, co zrobiło boost (prawie).

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

Stosowanie:

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

Powyższy kod działa najlepiej do inicjowania zmiennych globalnych lub statycznych elementów klasy, które należy zainicjować i nie masz pojęcia, kiedy zostanie najpierw użyty, ale chcesz się upewnić, że wartości są w nim dostępne.

Jeśli powiedzmy, musisz wstawić elementy do istniejącej std :: map ... oto kolejna klasa dla Ciebie.

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

Stosowanie:

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

Zobacz to w działaniu z GCC 4.7.2 tutaj: http://ideone.com/3uYJiH

############### WSZYSTKO PONIŻEJ TO JEST OBSOLETE ###################

EDYCJA : Poniższa map_add_valuesklasa, która była oryginalnym rozwiązaniem, które zasugerowałem, zawiedzie, jeśli chodzi o GCC 4.5+. Zobacz powyższy kod, aby dowiedzieć się, jak dodać wartości do istniejącej mapy.


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

Stosowanie:

std :: map <int, int> moja_mapa;
// Później gdzieś wzdłuż kodu
map_add_values ​​<int, int> (moja_mapa) (1,2) (3,4) (5,6);

UWAGA: Poprzednio użyłem a operator []do dodania rzeczywistych wartości. Nie jest to możliwe, jak komentuje dalle.

###################### END OF OBSOLETE SECTION #############################

Vite Falcon
źródło
3
Używam twojej pierwszej próbki jako <int, string> do wiązania liczb błędów (z wyliczenia) z komunikatami - działa jak urok - dziękuję.
slashmais
1
operator[]bierze tylko jeden argument.
dalle
1
@dalle: Dobry połów! Z jakiegoś powodu myślałem, że przeciążeni [] operatorzy mogą zaakceptować więcej.
Vite Falcon
2
To fantastyczna odpowiedź. Szkoda, że ​​OP nigdy go nie wybrał. Zasługujesz na mega rekwizyty.
Thomas Thorogood
map_add_values ​​nie działa w gcc, który narzeka: 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

Oto inny sposób wykorzystania 2-elementowego konstruktora danych. Do inicjalizacji nie są potrzebne żadne funkcje. Nie ma kodu innej firmy (Boost), żadnych statycznych funkcji lub obiektów, żadnych sztuczek, po prostu prosty 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);

Odkąd napisałem tę odpowiedź, C ++ 11 jest niedostępny. Możesz teraz bezpośrednio inicjalizować kontenery STL za pomocą nowej funkcji listy inicjalizacyjnej:

const MyMap myMap = { {"hello", 42}, {"world", 88} };
Brian Neal
źródło
25

Na przykład:

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

Jeśli map jest członkiem danych klasy, możesz zainicjować go bezpośrednio w nagłówku w następujący sposób (od 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
        };
}; 
isnullxbh
źródło
24

Owinąłbym mapę wewnątrz obiektu statycznego i umieściłem kod inicjalizacji mapy w konstruktorze tego obiektu, w ten sposób masz pewność, że mapa zostanie utworzona przed wykonaniem kodu inicjalizacji.

Drealmer
źródło
1
Jestem z tobą w tym. Jest także odrobinę szybszy :)
QBziZ
2
Tad szybciej niż co? Globalny statyczny z inicjatorem? Nie, to nie jest (pamiętaj o RVO).
Pavel Minaev,
7
Niezła odpowiedź. Byłbym szczęśliwy, gdybym zobaczył przykładowy kod
Sungguk Lim
18

Chciałem po prostu podzielić się czystym obejściem 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;
użytkownik3826594
źródło
2
to nie działa na obiekt bez domyślnego konstruktora, należy wybrać metodę wstawiania IMHO
Alessandro Teruzzi
16

Możesz spróbować:

std::map <int, int> mymap = 
{
        std::pair <int, int> (1, 1),
        std::pair <int, int> (2, 2),
        std::pair <int, int> (2, 2)
};
Dmitrij Oberemchenko
źródło
1
Nie można używać list inicjalizujących z typami nieagregowanymi przed C ++ 11, w takim przypadku można również użyć krótszej składni {1, 2}zamiast std::pair<int, int>(1, 2).
Ferruccio
9

Jest to podobne do PierreBdRbez kopiowania mapy.

#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);
zirytowany
źródło
12
Prawdopodobnie i tak nie zostałby skopiowany.
GManNickG
2
ale w ten sposób mapa nie może być statyczna, prawda?
xmoex
6

Jeśli utknąłeś w C ++ 98 i nie chcesz używać boosta, oto rozwiązanie, którego używam, gdy muszę zainicjować mapę statyczną:

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 ] ) ] );
Emanuele Benedetti
źródło
-4

Masz tutaj bardzo dobre odpowiedzi, ale dla mnie wygląda to na przypadek „kiedy wszystko, co wiesz, to młot” ...

Najprostsza odpowiedź na pytanie, dlaczego nie ma standardowego sposobu inicjowania mapy statycznej, nie ma dobrego powodu, aby kiedykolwiek używać mapy statycznej ...

Mapa to struktura zaprojektowana do szybkiego wyszukiwania nieznanego zestawu elementów. Jeśli znasz elementy wcześniej, po prostu użyj tablicy C. Wprowadź wartości w sposób posortowany lub uruchom sortowanie na nich, jeśli nie możesz tego zrobić. Następnie można uzyskać wydajność dziennika (n), używając funkcji stl :: do zapętlania wpisów, dolna_możliwość / górna_możliwość. Kiedy testowałem to wcześniej, zwykle działają one co najmniej 4 razy szybciej niż mapa.

Korzyści są wiele razy ... - szybsza wydajność (* 4, zmierzyłem na wielu typach procesorów, zawsze około 4) - prostsze debugowanie. Po prostu łatwiej jest zobaczyć, co się dzieje z układem liniowym. - Trywialne wdrożenia operacji kopiowania, jeśli okaże się to konieczne. - Nie przydziela pamięci w czasie wykonywania, więc nigdy nie zgłasza wyjątku. - Jest to standardowy interfejs, dzięki czemu można go łatwo udostępniać w różnych bibliotekach DLL, językach itp.

Mógłbym kontynuować, ale jeśli chcesz więcej, spójrz na wiele blogów Stroustrup na ten temat.

użytkownik2185945
źródło
8
Wydajność to nie jedyny powód korzystania z mapy. Na przykład istnieje wiele przypadków, w których chcesz połączyć wartości razem (na przykład kod błędu z komunikatem o błędzie), a mapa sprawia, że ​​korzystanie i dostęp są stosunkowo proste. Ale link do tych wpisów na blogu może być interesujący, może robię coś złego.
MatthiasB,
5
Tablica jest znacznie łatwiejsza i ma wyższą wydajność, jeśli możesz jej użyć. Ale jeśli indeksy (klucze) nie są ciągłe i szeroko rozstawione, potrzebujesz mapy.
KarlU
1
A mapjest także przydatną formą do reprezentowania funkcji częściowej (funkcja w sensie matematycznym; ale także swego rodzaju w sensie programowania). Tablica tego nie robi. Nie można, powiedzmy, wyszukiwać danych z tablicy za pomocą łańcucha.
einpoklum
3
Twoja odpowiedź nie próbuje odpowiedzieć na ważne pytanie, a zamiast tego spekuluje na temat ograniczeń języka, proponuje rozwiązania różnych problemów, dlatego głosuj negatywnie. Prawdziwy scenariusz - mapowanie (ciągłe lub nie) kodów błędów biblioteki na ciągi tekstowe. W przypadku tablicy czas wyszukiwania wynosi O (n), co można poprawić poprzez statyczne odwzorowanie na O (log (n)).
Tosha
2
Jeśli rzeczywiście „nie ma dobrego powodu, aby kiedykolwiek używać mapy statycznej ...”, to jest bardzo dziwne, że w C ++ 11 dodano składnię (listy inicjalizujące), która ułatwia ich użycie.
ellisbben