Bagaimana cara menginisialisasi peta const statis pribadi di C ++?

108

Saya hanya perlu kamus atau array asosiatif string=> int.

Ada tipe peta C ++ untuk kasus ini.

Tetapi saya hanya membutuhkan satu peta untuk semua contoh (-> statis) dan peta ini tidak dapat diubah (-> const);

Saya telah menemukan cara ini dengan perpustakaan boost

 std::map<int, char> example = 
      boost::assign::map_list_of(1, 'a') (2, 'b') (3, 'c');

Apakah ada solusi lain tanpa lib ini? Saya telah mencoba sesuatu seperti ini, tetapi selalu ada beberapa masalah dengan inisialisasi peta.

class myClass{
private:
    static map<int,int> create_map()
        {
          map<int,int> m;
          m[1] = 2;
          m[3] = 4;
          m[5] = 6;
          return m;
        }
    static map<int,int> myMap =  create_map();

};
Meloun
sumber
1
Masalah apa yang Anda rujuk? Apakah Anda mencoba menggunakan peta ini dari variabel / konstanta statis global lain?
Péter Török
Itu bukan string array asosiatif => int, Anda memetakan int ke karakter. v = k + 'a' - 1.
Johnsyweb

Jawaban:

107
#include <map>
using namespace std;

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

};

const map<int,int> A:: myMap =  A::create_map();

int main() {
}

sumber
3
+1 untuk kesederhanaan, tentunya menggunakan Boost.Assigndesain like juga lumayan rapi :)
Matthieu M.
5
+1, terima kasih. Catatan: Saya harus meletakkan baris inisialisasi di file implementasi saya; meninggalkannya di file header memberi saya kesalahan karena beberapa definisi (kode inisialisasi akan berjalan setiap kali header disertakan di suatu tempat).
System.Cats.Lol
1
Dengan g ++ v4.7.3, mengkompilasi ini, sampai saya tambahkan cout << A::myMap[1];ke dalam main(). Ini memberi kesalahan. Kesalahan tidak terjadi jika saya menghapus constqualifier, jadi saya kira map operator[]tidak dapat menangani const map, setidaknya, tidak dalam implementasi g ++ pustaka C ++.
Craig McQueen
2
Kesalahannya adalah:const_map.cpp:22:23: error: passing ‘const std::map<int, int>’ as ‘this’ argument of ‘std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = int; _Tp = int; _Compare = std::less<int>; _Alloc = std::allocator<std::pair<const int, int> >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = int; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = int]’ discards qualifiers [-fpermissive]
Craig McQueen
4
Memang, operator peta [] tidak bisa beroperasi pada peta const karena operator tersebut membuat entri referensi jika tidak ada (karena mengembalikan referensi ke nilai yang dipetakan). C ++ 11 memperkenalkan metode at (KeyValT key) yang memungkinkan Anda mengakses item dengan kunci yang diberikan, memberikan pengecualian jika tidak ada. ( en.cppreference.com/w/cpp/container/map/at ) Metode ini akan bekerja pada instance const tetapi tidak dapat digunakan untuk menyisipkan elemen pada instance non-const (seperti halnya operator []).
mbargiel
108

Standar C ++ 11 memperkenalkan inisialisasi seragam yang membuatnya lebih sederhana jika kompiler Anda mendukungnya:

//myClass.hpp
class myClass {
  private:
    static map<int,int> myMap;
};


//myClass.cpp
map<int,int> myClass::myMap = {
   {1, 2},
   {3, 4},
   {5, 6}
};

Lihat juga bagian ini dari Professional C ++ , di unordered_maps.

David C. Bishop
sumber
Apakah kita membutuhkan tanda yang sama di file cpp?
phoad
@phoad: Tanda sama dengan tidak berguna.
Dibenci
Terima kasih telah menunjukkan penggunaannya. Sangat membantu untuk memahami bagaimana memodifikasi variabel statis.
Pengguna9102d82
12

Saya melakukannya! :)

Bekerja dengan baik tanpa C ++ 11

class MyClass {
    typedef std::map<std::string, int> MyMap;

    struct T {
        const char* Name;
        int Num;

        operator MyMap::value_type() const {
            return std::pair<std::string, int>(Name, Num);
        }
    };

    static const T MapPairs[];
    static const MyMap TheMap;
};

const MyClass::T MyClass::MapPairs[] = {
    { "Jan", 1 }, { "Feb", 2 }, { "Mar", 3 }
};

const MyClass::MyMap MyClass::TheMap(MapPairs, MapPairs + 3);
pengguna2622030
sumber
11

Jika Anda merasa boost::assign::map_list_ofberguna, tetapi tidak dapat menggunakannya karena alasan tertentu, Anda dapat menulis sendiri :

template<class K, class V>
struct map_list_of_type {
  typedef std::map<K, V> Map;
  Map data;
  map_list_of_type(K k, V v) { data[k] = v; }
  map_list_of_type& operator()(K k, V v) { data[k] = v; return *this; }
  operator Map const&() const { return data; }
};
template<class K, class V>
map_list_of_type<K, V> my_map_list_of(K k, V v) {
  return map_list_of_type<K, V>(k, v);
}

int main() {
  std::map<int, char> example = 
    my_map_list_of(1, 'a') (2, 'b') (3, 'c');
  cout << example << '\n';
}

Sangat berguna untuk mengetahui bagaimana hal-hal seperti itu bekerja, terutama ketika mereka sangat pendek, tetapi dalam kasus ini saya akan menggunakan sebuah fungsi:

a.hpp

struct A {
  static map<int, int> const m;
};

a.cpp

namespace {
map<int,int> create_map() {
  map<int, int> m;
  m[1] = 2; // etc.
  return m;
}
}

map<int, int> const A::m = create_map();
Yu Hao
sumber
6

Pendekatan berbeda untuk masalah ini:

struct A {
    static const map<int, string> * singleton_map() {
        static map<int, string>* m = NULL;
        if (!m) {
            m = new map<int, string>;
            m[42] = "42"
            // ... other initializations
        }
        return m;
    }

    // rest of the class
}

Ini lebih efisien, karena tidak ada salinan satu tipe dari tumpukan ke heap (termasuk konstruktor, penghancur pada semua elemen). Apakah ini penting atau tidak tergantung pada kasus penggunaan Anda. Tidak masalah dengan string! (tetapi Anda mungkin atau mungkin tidak menemukan versi "lebih bersih" ini)

ypnos
sumber
3
RVO menghilangkan penyalinan di jawaban saya dan Neil.
6

Jika peta hanya berisi entri yang diketahui pada waktu kompilasi dan kunci peta adalah bilangan bulat, maka Anda tidak perlu menggunakan peta sama sekali.

char get_value(int key)
{
    switch (key)
    {
        case 1:
            return 'a';
        case 2:
            return 'b';
        case 3:
            return 'c';
        default:
            // Do whatever is appropriate when the key is not valid
    }
}
Matthew T. Staebler
sumber
5
1 untuk menunjukkan bahwa peta tidak diperlukan, namun, Anda tidak dapat mengulanginya
Viktor Sehr
4
Itu switchsangat buruk. Mengapa tidak return key + 'a' - 1?
Johnsyweb
12
@Bayu_joo Saya berasumsi bahwa pemetaan yang diberikan oleh poster asli hanya sebagai contoh dan bukan indikasi dari pemetaan yang sebenarnya dia miliki. Oleh karena itu, saya juga akan berasumsi bahwa return key + 'a' - 1itu tidak akan berhasil untuk pemetaan sebenarnya.
Matthew T. Staebler
3

Anda bisa mencoba ini:

MyClass.h

class MyClass {
private:
    static const std::map<key, value> m_myMap; 
    static const std::map<key, value> createMyStaticConstantMap();
public:
    static std::map<key, value> getMyConstantStaticMap( return m_myMap );
}; //MyClass

MyClass.cpp

#include "MyClass.h"

const std::map<key, value> MyClass::m_myMap = MyClass::createMyStaticConstantMap();

const std::map<key, value> MyClass::createMyStaticConstantMap() {
    std::map<key, value> mMap;
    mMap.insert( std::make_pair( key1, value1 ) );
    mMap.insert( std::make_pair( key2, value2 ) );
    // ....
    mMap.insert( std::make_pair( lastKey, lastValue ) ); 
    return mMap;
} // createMyStaticConstantMap

Dengan implementasi ini, peta statis konstan kelas Anda adalah anggota privat dan dapat diakses oleh kelas lain menggunakan metode get publik. Jika tidak, karena konstan dan tidak dapat diubah, Anda dapat menghapus metode get publik dan memindahkan variabel peta ke bagian kelas publik. Namun saya akan membiarkan metode createMap pribadi atau dilindungi jika pewarisan dan atau polimorfisme diperlukan. Berikut beberapa contoh penggunaan.

 std::map<key,value> m1 = MyClass::getMyMap();
 // then do work on m1 or
 unsigned index = some predetermined value
 MyClass::getMyMap().at( index ); // As long as index is valid this will 
 // retun map.second or map->second value so if in this case key is an
 // unsigned and value is a std::string then you could do
 std::cout << std::string( MyClass::getMyMap().at( some index that exists in map ) ); 
// and it will print out to the console the string locted in the map at this index. 
//You can do this before any class object is instantiated or declared. 

 //If you are using a pointer to your class such as:
 std::shared_ptr<MyClass> || std::unique_ptr<MyClass>
 // Then it would look like this:
 pMyClass->getMyMap().at( index ); // And Will do the same as above
 // Even if you have not yet called the std pointer's reset method on
 // this class object. 

 // This will only work on static methods only, and all data in static methods must be available first.

Saya telah mengedit posting asli saya, tidak ada yang salah dengan kode asli yang saya posting untuk itu terkompilasi, dibangun dan berjalan dengan benar, hanya saja versi pertama saya yang saya sajikan sebagai jawaban peta dinyatakan sebagai publik dan peta itu const tetapi tidak statis.

Francis Cugler
sumber
2

Jika Anda menggunakan kompiler yang masih tidak mendukung inisialisasi universal atau Anda memiliki reservasi untuk menggunakan Boost, alternatif lain yang memungkinkan adalah sebagai berikut

std::map<int, int> m = [] () {
    std::pair<int,int> _m[] = {
        std::make_pair(1 , sizeof(2)),
        std::make_pair(3 , sizeof(4)),
        std::make_pair(5 , sizeof(6))};
    std::map<int, int> m;
    for (auto data: _m)
    {
        m[data.first] = data.second;
    }
    return m;
}();
Abhijit
sumber
0

Panggilan fungsi tidak bisa muncul dalam ekspresi konstan.

coba ini: (hanya sebuah contoh)

#include <map>
#include <iostream>

using std::map;
using std::cout;

class myClass{
 public:
 static map<int,int> create_map()
    {
      map<int,int> m;
      m[1] = 2;
      m[3] = 4;
      m[5] = 6;
      return m;
    }
 const static map<int,int> myMap;

};
const map<int,int>myClass::myMap =  create_map();

int main(){

   map<int,int> t=myClass::create_map();
   std::cout<<t[1]; //prints 2
}
Prasoon Saurav
sumber
6
Sebuah fungsi tentunya dapat digunakan untuk menginisialisasi sebuah objek const.
Dalam kode OP static map<int,int> myMap = create_map();salah.
Prasoon Saurav
3
Kode dalam pertanyaan salah, kita semua setuju untuk itu, tetapi itu tidak ada hubungannya dengan 'ekspresi konstan' seperti yang Anda katakan dalam jawaban ini, melainkan dengan fakta bahwa Anda hanya dapat menginisialisasi anggota statis konstan kelas di deklarasi jika mereka berjenis integer atau enum. Untuk semua tipe lainnya, inisialisasi harus dilakukan dalam definisi anggota dan bukan deklarasi.
David Rodríguez - dribeas
Jawaban Neil dikompilasi dengan g ++. Namun, saya ingat mengalami beberapa masalah dengan pendekatan ini di versi GNU toolchain sebelumnya. Apakah ada jawaban yang benar universal?
Basilevs
1
@Prasoon: Saya tidak tahu apa yang dikatakan kompilator, tetapi kesalahan dalam kode pertanyaan adalah menginisialisasi atribut anggota konstan dari tipe kelas dalam deklarasi kelas, terlepas dari apakah inisialisasi adalah ekspresi konstan atau tidak. Jika Anda mendefinisikan kelas: struct testdata { testdata(int){} }; struct test { static const testdata td = 5; }; testdata test::td;itu akan gagal untuk dikompilasi bahkan jika inisialisasi dilakukan dengan ekspresi konstan ( 5). Artinya, 'ekspresi konstan' tidak relevan dengan kebenaran (atau kurangnya itu) dari kode awal.
David Rodríguez - dribeas
-2

Saya sering menggunakan pola ini dan menyarankan Anda untuk menggunakannya juga:

class MyMap : public std::map<int, int>
{
public:
    MyMap()
    {
        //either
        insert(make_pair(1, 2));
        insert(make_pair(3, 4));
        insert(make_pair(5, 6));
        //or
        (*this)[1] = 2;
        (*this)[3] = 4;
        (*this)[5] = 6;
    }
} const static my_map;

Tentu itu tidak terlalu bisa dibaca, tapi tanpa libs lain yang terbaik yang bisa kita lakukan. Juga tidak akan ada operasi yang berlebihan seperti menyalin dari satu peta ke peta lain seperti dalam upaya Anda.

Ini bahkan lebih berguna di dalam fungsi: Dari pada:

void foo()
{
   static bool initComplete = false;
   static Map map;
   if (!initComplete)
   {
      initComplete = true;
      map= ...;
   }
}

Gunakan yang berikut ini:

void bar()
{
    struct MyMap : Map
    {
      MyMap()
      {
         ...
      }
    } static mymap;
}

Tidak hanya Anda tidak perlu di sini untuk berurusan dengan variabel boolean lagi, Anda tidak akan menyembunyikan variabel global yang diperiksa jika penginisialisasi variabel statis di dalam fungsi sudah dipanggil.

Pavel Chikulaev
sumber
6
Warisan harus menjadi alat pilihan terakhir, bukan yang pertama.
Kompiler yang mendukung RVO menghilangkan penyalinan yang berlebihan dengan versi fungsi. Semantik gerakan C ++ 0x menghilangkan sisanya, setelah tersedia. Bagaimanapun, saya ragu itu hampir menjadi hambatan.
Roger, saya sangat memahami RVO, && dan semantik bergerak. Ini adalah solusi untuk saat ini dalam jumlah kode dan entitas yang minimal. Ditambah semua fitur C ++ 0x tidak akan membantu dengan objek statis di dalam contoh fungsi karena kami tidak diizinkan untuk mendefinisikan fungsi di dalam fungsi.
Pavel Chikulaev