Menginisialisasi std :: map statis <int, int> dalam C ++

447

Apa cara yang tepat untuk menginisialisasi peta statis? Apakah kita memerlukan fungsi statis yang akan menginisialisasi itu?

Nithin
sumber

Jawaban:

619

Menggunakan C ++ 11:

#include <map>
using namespace std;

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

Menggunakan 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
sumber
115
Setiap kali saya melihat sesuatu seperti itu dilakukan dengan C ++, saya memikirkan semua kode template yang mengerikan yang harus ada di belakangnya. Contoh yang baik!
Greg Hewgill
34
Keindahan dari semua kode templat menghebohkan yang mengimplementasikan utilitas ini adalah bahwa ia dikemas dengan rapi di perpustakaan dan pengguna akhir jarang perlu berurusan dengan kompleksitas.
Steve Guidi
45
@QBziZ: Jika perusahaan Anda menolak menggunakan Boost dengan alasan itu tidak menjadi "cukup standar", saya ingin tahu apa perpustakaan C ++ akan menjadi "cukup standar". Boost yang pendamping standar untuk C ++ coder.
DevSolar
47
Masalah saya dengan Boost (di sini, dan di tempat lain) adalah Anda sering dapat melanjutkan tanpanya (dalam hal ini dengan C ++ 11 atau sebelum C ++ 11 dengan sebuah fungsi ). Boost menambahkan overhead waktu kompilasi yang signifikan, memiliki banyak file untuk diparkir ke dalam repositori Anda (dan harus menyalin / zip / mengekstrak jika Anda membuat arsip). Itulah alasan saya mencoba untuk tidak menggunakannya. Saya tahu Anda dapat memilih file apa yang akan disertakan / tidak disertakan, tetapi Anda biasanya tidak ingin perlu khawatir tentang dependensi lintas Boost dengan dirinya sendiri sehingga Anda hanya menyalin semuanya.
bobobobo
7
Masalah saya dengan Boost adalah bahwa ia sering memiliki beberapa dependensi perpustakaan baru, yang umumnya berarti LEBIH paket yang perlu diinstal untuk bekerja dengan benar. Kami sudah membutuhkan libstdc ++. Misalnya, pustaka Boost ASIO, membutuhkan setidaknya 2 pustaka baru (mungkin lebih) yang perlu diinstal. C ++ 11/14 memang membuatnya jauh lebih mudah untuk tidak membutuhkan Peningkatan.
Rahly
135

Cara terbaik adalah menggunakan fungsi:

#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
sumber
18
Kenapa ini yang 'terbaik'? Mengapa misalnya lebih baik daripada jawaban @ Dreamer?
Marquis of Lorne
6
Saya pikir itu "terbaik" karena sangat sederhana dan tidak tergantung pada struktur lain yang ada (seperti Boost :: Assign atau implementasi ulang itu). Dan dibandingkan dengan jawaban @ Dreamer, well, saya menghindari membuat seluruh struktur hanya untuk menginisialisasi peta ...
PierreBdR
3
Perhatikan ada bahaya di sini . externvariabel tidak akan memiliki nilai yang benar dalam "sebelum konstruk run-time utama" ini jika kompilator hanya melihat externdeklarasi, tetapi belum mengalami definisi variabel yang sebenarnya .
bobobobo
5
Tidak, bahayanya adalah tidak ada yang mengatakan urutan variabel statis harus diinisialisasi (setidaknya di seluruh unit kompilasi). Tapi ini bukan masalah yang terkait dengan pertanyaan ini. Ini adalah masalah umum dengan variabel statis.
PierreBdR
5
tidak ada peningkatan DAN tidak ada C ++ 11 => +1. Perhatikan bahwa fungsi dapat digunakan untuk menginisialisasi const map<int,int> m = create_map()(dan sebagainya, menginisialisasi anggota const dari kelas dalam daftar inisialisasi:struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
ribamar
115

Ini bukan masalah rumit untuk membuat sesuatu yang mirip dengan meningkatkan. Inilah kelas dengan hanya tiga fungsi, termasuk konstruktor, untuk mereplikasi apa yang telah dilakukan boost (hampir).

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

Pemakaian:

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

Kode di atas berfungsi paling baik untuk inisialisasi variabel global atau anggota statis suatu kelas yang perlu diinisialisasi dan Anda tidak tahu kapan itu digunakan terlebih dahulu tetapi Anda ingin memastikan bahwa nilai-nilai tersedia di dalamnya.

Jika mengatakan, Anda harus memasukkan elemen ke std :: map yang ada ... inilah kelas lain untuk Anda.

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

Pemakaian:

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

Lihat beraksi dengan GCC 4.7.2 di sini: http://ideone.com/3uYJiH

############### SEMUA DI BAWAH INI ADALAH OBSOLETE #################

EDIT : map_add_valuesKelas di bawah ini, yang merupakan solusi asli yang saya sarankan, akan gagal ketika datang ke GCC 4.5+. Silakan lihat kode di atas untuk cara menambahkan nilai ke peta yang ada.


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

Pemakaian:

std :: map <int, int> my_map;
// Kemudian di suatu tempat di sepanjang kode
map_add_values ​​<int, int> (my_map) (1,2) (3,4) (5,6);

CATATAN: Sebelumnya saya menggunakan a operator []untuk menambahkan nilai aktual. Ini tidak mungkin seperti dikomentari oleh dalle.

#################### AKHIR DARI BAGIAN OBSOLETE ####################

Vite Falcon
sumber
3
Saya menggunakan sampel pertama Anda sebagai <int, string> untuk mengikat angka kesalahan (dari enum) dengan pesan - itu berfungsi seperti pesona - terima kasih.
slashmais
1
operator[]hanya membutuhkan satu argumen.
dalle
1
@dalle: Tangkapan bagus! Untuk beberapa alasan saya pikir operator [] yang kelebihan beban bisa menerima lebih banyak.
Vite Falcon
2
Ini jawaban yang fantastis. Sayang sekali OP tidak pernah memilihnya. Anda layak mendapatkan alat peraga mega.
Thomas Thorogood
map_add_values ​​tidak berfungsi dalam gcc, yang komplain: 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

Berikut ini cara lain yang menggunakan konstruktor data 2-elemen. Tidak diperlukan fungsi untuk menginisialisasi itu. Tidak ada kode pihak ke-3 (Boost), tidak ada fungsi atau objek statis, tidak ada trik, cukup sederhana 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);

Karena saya menulis jawaban ini C ++ 11 keluar. Anda sekarang dapat langsung menginisialisasi wadah STL menggunakan fitur daftar penginisialisasi baru:

const MyMap myMap = { {"hello", 42}, {"world", 88} };
Brian Neal
sumber
25

Sebagai contoh:

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

Jika peta adalah anggota data kelas, Anda dapat menginisialisasi langsung di header dengan cara berikut (karena 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
sumber
24

Saya akan membungkus peta di dalam objek statis, dan meletakkan kode inisialisasi peta di konstruktor objek ini, dengan cara ini Anda yakin peta dibuat sebelum kode inisialisasi dijalankan.

Drealmer
sumber
1
Aku bersamamu dalam hal ini. Ini juga sedikit lebih cepat :)
QBziZ
2
Sedikit lebih cepat dari apa? Statis global dengan penginisialisasi? Tidak, itu tidak (ingat tentang RVO).
Pavel Minaev
7
Jawaban bagus. Saya akan senang jika saya melihat contoh kode yang sebenarnya
Sungguk Lim
18

Hanya ingin berbagi C ++ 98 yang murni untuk bekerja:

#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;
pengguna3826594
sumber
2
ini tidak berfungsi untuk objek tanpa konstruktor default, metode insert harus lebih disukai IMHO
Alessandro Teruzzi
16

Anda dapat mencoba:

std::map <int, int> mymap = 
{
        std::pair <int, int> (1, 1),
        std::pair <int, int> (2, 2),
        std::pair <int, int> (2, 2)
};
Dmitry Oberemchenko
sumber
1
Anda tidak dapat menggunakan initializer daftar dengan jenis non-agregat sebelum C ++ 11, dalam hal ini Anda mungkin juga menggunakan sintaks yang lebih pendek {1, 2}bukan std::pair<int, int>(1, 2).
Ferruccio
9

Ini mirip dengan PierreBdR, tanpa menyalin peta.

#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);
bengkak
sumber
12
Mungkin itu tidak akan disalin.
GManNickG
2
tetapi cara ini peta tidak bisa menjadi statis, kan?
xmoex
6

Jika Anda terjebak dengan C ++ 98 dan tidak ingin menggunakan boost, di sini ada solusi yang saya gunakan ketika saya perlu menginisialisasi peta statis:

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
sumber
-4

Anda memiliki beberapa jawaban yang sangat bagus di sini, tapi bagi saya, sepertinya ini adalah kasus "ketika yang Anda tahu adalah palu" ...

Jawaban paling sederhana mengapa tidak ada cara standar untuk menginisialisasi peta statis, adalah tidak ada alasan yang baik untuk menggunakan peta statis ...

Peta adalah struktur yang dirancang untuk pencarian cepat, dari serangkaian elemen yang tidak diketahui. Jika Anda tahu elemen sebelumnya, cukup gunakan C-array. Masukkan nilai-nilai dengan cara diurutkan, atau jalankan mengurutkannya, jika Anda tidak bisa melakukan ini. Anda kemudian bisa mendapatkan kinerja log (n) dengan menggunakan fungsi stl :: untuk menambahkan entri, lower_bound / upper_bound. Ketika saya telah menguji ini sebelumnya, mereka biasanya melakukan setidaknya 4 kali lebih cepat daripada peta.

Keuntungannya banyak lipat ... - kinerja lebih cepat (* 4, saya sudah mengukur pada banyak jenis CPU, selalu sekitar 4) - debugging sederhana. Hanya lebih mudah untuk melihat apa yang terjadi dengan tata letak linier. - Implementasi sepele dari operasi salinan, jika itu menjadi perlu. - Ini tidak mengalokasikan memori pada saat dijalankan, jadi tidak akan pernah membuang pengecualian. - Ini adalah antarmuka standar, dan sangat mudah untuk dibagikan, DLL, atau bahasa, dll.

Saya bisa melanjutkan, tetapi jika Anda menginginkan lebih, mengapa tidak melihat banyak blog Stroustrup tentang hal ini.

pengguna2185945
sumber
8
Kinerja bukan satu-satunya alasan untuk menggunakan peta. Misalnya, ada banyak kasus, di mana Anda ingin menautkan nilai bersama-sama (misalnya, kode kesalahan dengan pesan kesalahan), dan peta membuat penggunaan dan akses relatif sederhana. Tapi tautan ke entri blog ini mungkin menarik, mungkin saya melakukan sesuatu yang salah.
MatthiasB
5
Array jauh lebih mudah dan memiliki kinerja yang lebih tinggi jika Anda dapat menggunakannya. Tetapi jika indeks (kunci) tidak berdekatan, dan ditempatkan secara luas, Anda memerlukan peta.
KarlU
1
A mapjuga merupakan bentuk yang berguna untuk mewakili fungsi parsial (fungsi dalam arti matematika; tetapi juga, semacam, dalam pengertian pemrograman). Array tidak melakukan itu. Anda tidak bisa, katakanlah, mencari data dari array menggunakan string.
einpoklum
3
Jawaban Anda tidak berusaha menjawab pertanyaan yang valid, dan malah berspekulasi tentang keterbatasan bahasa, mengusulkan solusi untuk masalah yang berbeda, karenanya undur suara. Skenario nyata - pemetaan (terus menerus atau tidak) kode kesalahan pustaka ke string teks. Dengan larik, waktu pencarian adalah O (n), yang dapat ditingkatkan dengan pemetaan statis ke O (log (n)).
Tosha
2
Jika memang "tidak ada alasan bagus untuk menggunakan peta statis ..." maka sangat aneh bahwa sintaks (daftar penginisialisasi) yang membuatnya mudah digunakan ditambahkan dalam C ++ 11.
ellisbben