C ++ 'da statik bir std :: map <int, int> başlatılıyor


449

Statik bir haritayı başlatmanın doğru yolu nedir? Başlatacak statik bir işleve ihtiyacımız var mı?

Yanıtlar:


620

C ++ 11 kullanma:

#include <map>
using namespace std;

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

Kullanılması 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 ++ ile yapılan böyle bir şey her gördüğümde, arkasında olması gereken tüm korkunç şablon kodunu düşünüyorum. İyi örnek!
Greg Hewgill

34
Bu yardımcı programları uygulayan tüm korkunç şablon kodunun güzelliği, bir kütüphanede düzgün bir şekilde kapsüllenmiş olması ve son kullanıcının nadiren karmaşıklıkla uğraşması gerektiğidir.
Steve Guidi

45
@ QBziZ: Şirketiniz "yeterince standart" olmadığı için Boost'u kullanmayı reddederse, C ++ kütüphanesinin "yeterince standart" olacağını merak ediyorum . Boost olan C ++ kodlayıcı için standart arkadaşı.
DevSolar

47
Boost (burada ve başka yerlerde) ile benim sorunum, genellikle onsuz (bu durumda C ++ 11 ile veya bir işlevi ile C ++ 11 önce ) alabilirsiniz. Boost, önemli bir derleme süresi ekler, deponuza park etmek için tonlarca dosyaya sahipti (ve bir arşiv oluşturuyorsanız / zip / extract kopyalamak zorundasınız). Bu yüzden kullanmamaya çalışıyorum. Hangi dosyaları dahil edeceğinizi / içermeyeceğinizi seçebileceğinizi biliyorum, ancak genellikle Boost'un kendisiyle olan çapraz bağımlılıkları hakkında endişelenmek zorunda kalmazsınız, böylece etraftaki her şeyi kopyalarsınız.
bobobobo

7
Boost ile ilgili sorunum, genellikle birkaç yeni kütüphane bağımlılığına sahip olmasıdır, bu da genellikle düzgün çalışması için yüklenmesi gereken DAHA FAZLA paket anlamına gelir. Zaten libstdc ++ 'a ihtiyacımız var. Örneğin, Boost ASIO kitaplığı, yüklenmesi gereken en az 2 yeni kitaplık (muhtemelen daha fazla) gerektirir. C ++ 11/14, Boost'a ihtiyaç duymayı çok daha kolay hale getirir.
Rahly

135

En iyi yol bir işlevi kullanmaktır:

#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
Bu neden 'en iyi'? Örneğin, @ Dreamer'ın cevabından neden daha iyi?
Lorne Marquis

6
Bence "en iyi" çünkü gerçekten basit ve mevcut diğer yapılara bağlı değil (Boost :: Atama veya yeniden uygulanması gibi). Ve @ Dreamer'ın cevabı ile karşılaştırıldığında, sadece bir haritayı başlatmak için bütün bir yapı oluşturmaktan kaçınırım ...
PierreBdR

3
Burada bir tehlike olduğuna dikkat edin . Derleyici yalnızca bildirimi gördüyse , ancak gerçek değişken tanımına henüz girmediyse, değişkenlerextern bu "ana çalışma zamanı yapıcısından önce" doğru değerlerine sahip olmayacaktır . extern
bobobobo

5
Hayır, tehlike, statik değişkenlerin hangi sırayla başlatılması gerektiğini (en azından derleme birimlerinde) söyleyecek hiçbir şey olmamasıdır. Ancak bu, bu soruyla bağlantılı bir sorun değildir. Bu, statik değişkenlerle ilgili genel bir sorundur.
PierreBdR

5
destek yok VE C ++ 11 => +1 yok. Bu işlevin const map<int,int> m = create_map()başlatma listesinde bir sınıfın const üyelerini başlatmak için kullanılabileceğine dikkat edin :struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
ribamar

115

Arttırmaya benzer bir şey yapmak karmaşık bir mesele değil. Burada, yapıcıyı da dahil etmek için sadece üç işlevi olan bir sınıf var (neredeyse) güçlendirmeyi yaptı.

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

Kullanımı:

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

Yukarıdaki kod, bir sınıfın başlatılması gereken global değişkenlerin veya statik üyelerin başlatılması için en iyi sonucu verir ve ilk kez ne zaman kullanıldığına dair hiçbir fikriniz yoktur, ancak değerlerin içinde bulunduğundan emin olmak istersiniz.

Varsa, mevcut bir std :: haritasına eleman eklemeniz gerekir ... işte sizin için başka bir sınıf.

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

Kullanımı:

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

GCC 4.7.2 ile çalışırken görün: http://ideone.com/3uYJiH

############### BU AŞAĞIDAKİ HER ŞEY OBSOLET'tir #################

EDIT : map_add_valuesÖnerdiğim orijinal çözüm olan aşağıdaki sınıf, GCC 4.5+ söz konusu olduğunda başarısız olurdu. Mevcut haritaya nasıl değer ekleyeceğinizi öğrenmek için lütfen yukarıdaki koda bakın .


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

Kullanımı:

std :: map <int, int> my_map;
// Daha sonra kod boyunca bir yerlerde
map_add_values ​​<int, int> (my_map) (1,2) (3,4) (5,6);

NOT: Daha önce operator []gerçek değerleri eklemek için a kullandım . Bu dalle tarafından yorumlandığı gibi mümkün değildir.

##################### OBSOLETE BÖLÜMÜ SONU ######################


3
İlk örnek olarak hata numaralarını (bir numaradan) iletileri ile bağlamak için <int, string> olarak kullanıyorum - bir cazibe gibi çalışıyor - teşekkür ederim.
slashmais

1
operator[]sadece tek bir argüman alır.
dalle

1
@dalle: İyi yakaladın! Nedense aşırı yüklenmiş [] operatörlerin daha fazlasını kabul edebileceğini düşündüm.
Vite Falcon

2
Bu harika bir cevap. OP'nin hiç seçmediği bir utanç. Mega sahne hak ediyorsun.
Thomas Thorogood

map_add_values ​​gcc'de çalışmaz, bu da şikayet eder: 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

İşte 2 öğeli veri yapıcısını kullanan başka bir yol. Başlatılması için herhangi bir işlev gerekmez. Hiçbir 3. parti kodu (Boost), statik işlev veya nesne yok, hile yok, sadece basit 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);

Bu cevabı yazdığım için C ++ 11 çıktı. Artık yeni başlatıcı listesi özelliğini kullanarak STL kapsayıcılarını doğrudan başlatabilirsiniz:

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

25

Örneğin:

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

Harita bir sınıfın veri üyesiyse, haritayı doğrudan başlıkta şu şekilde başlatabilirsiniz (C ++ 17'den beri):

// 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

Haritayı statik bir nesnenin içine sarar ve harita başlatma kodunu bu nesnenin yapıcısına koyarım, böylece başlatma kodu yürütülmeden önce haritanın oluşturulduğundan emin olabilirsiniz.


1
Bu konuda seninleyim. Aynı zamanda biraz daha hızlı :)
QBziZ

2
Ne daha hızlı tad? Bir başlatıcı ile küresel bir statik? Hayır, değil (RVO'yu hatırla).
Pavel Minaev

7
Güzel cevap. Gerçek örnek kodu
görürsem

18

Sadece saf bir C ++ 98 çalışmasını paylaşmak istedim:

#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
bu varsayılan oluşturucu olmadan nesne için çalışmaz, ekleme yöntemi tercih edilmelidir IMHO
Alessandro Teruzzi

16

Deneyebilirsin:

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'den önce toplama olmayan türlerle başlatıcı listelerini kullanamazsınız; bu durumda {1, 2}bunun yerine daha kısa sözdizimini de kullanabilirsiniz std::pair<int, int>(1, 2).
Ferruccio

9

Bu, PierreBdRharitayı kopyalamadan benzer .

#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
Muhtemelen yine de kopyalanamazdı.
GManNickG

2
ama bu şekilde harita statik sabit olamazdı, değil mi?
xmoex

6

C ++ 98 ile sıkışmış ve boost kullanmak istemiyorsanız, burada statik bir harita başlatmak gerektiğinde kullandığım çözüm var:

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

Burada çok iyi cevaplarınız var, ama bana göre, "bildiğiniz her şey bir çekiç olduğunda" bir durum gibi görünüyor ...

Statik bir haritayı başlatmak için standart bir yolun olmamasının en basit cevabı, statik bir haritayı kullanmak için iyi bir neden yoktur ...

Harita, bilinmeyen bir dizi öğenin hızlı aranması için tasarlanmış bir yapıdır. Önceden öğeleri biliyorsanız, sadece bir C-dizisi kullanın. Değerleri sıralı bir şekilde girin veya bunu yapamıyorsanız sıralama üzerinde çalıştırın. Daha sonra, alt döngülü / üst_bound girişleri artırmak için stl :: fonksiyonlarını kullanarak log (n) performansını elde edebilirsiniz. Bunu daha önce test ettiğimde, normalde bir haritadan en az 4 kat daha hızlı performans gösterirler.

Avantajları çok katlıdır ... - daha hızlı performans (* 4, birçok CPU tipinde ölçtüm, her zaman 4 civarında) - daha basit hata ayıklama. Doğrusal bir düzende neler olduğunu görmek daha kolaydır. - Gerekirse, kopyalama işlemlerinin önemsiz uygulamaları. - Çalışma zamanında bellek ayırmaz, bu yüzden asla bir istisna atmaz. - Bu standart bir arabirimdir ve DLL, veya diller vb. Arasında paylaşmak çok kolaydır.

Devam edebilirdim, ancak daha fazlasını istiyorsanız, neden Stroustrup'un konuyla ilgili birçok bloguna bakmıyorsunuz?


8
Harita kullanmanın tek nedeni performans değildir. Örneğin, değerleri birbirine bağlamak istediğiniz birçok durum vardır (örneğin, hata mesajlı bir hata kodu) ve bir harita kullanımı ve erişimi nispeten basitleştirir. Ama bu blog girişlerinin bağlantısı ilginç olabilir, belki yanlış bir şey yapıyorum.
MatthiasB

5
Bir dizi çok daha kolaydır ve kullanabiliyorsanız daha yüksek performansa sahiptir. Ancak indeksler (anahtarlar) bitişik ve geniş aralıklı değilse, bir haritaya ihtiyacınız vardır.
KarlU

1
A mapaynı zamanda kısmi bir fonksiyonu (matematiksel anlamda fonksiyon; fakat aynı zamanda programlama anlamında) temsil etmek için de yararlı bir formdur. Bir dizi bunu yapmaz. Dize kullanarak bir diziden veri arayamazsınız.
einpoklum

3
Cevabınız geçerli soruyu cevaplamaya çalışmaz ve bunun yerine dilin sınırlamaları hakkında spekülasyonlar yapar, farklı sorunlara çözümler önerir, dolayısıyla aşağıya doğru oy verir. Gerçek bir senaryo - kütüphane hata kodlarını metin dizeleriyle eşleştirme (sürekli ya da değil). Dizi ile arama süresi O (n) 'dir ve O (log (n))' e statik eşleme ile geliştirilebilir.
Tosha

2
Gerçekten de "statik bir harita kullanmak için iyi bir neden yok ..." ise, C ++ 11'de sözdiziminin (başlatıcı listeleri) kullanımını kolaylaştıran çok gariptir.
ellisbben
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.