C ++ 'da özel bir statik sabit haritası nasıl başlatılır?


108

Sadece sözlüğe veya ilişkisel dizi string=> ihtiyacım var int.

Bu durum için tip eşlemi C ++ vardır.

Ancak tüm örnekler için (-> statik) yalnızca bir haritaya ihtiyacım var ve bu harita değiştirilemez (-> const);

Bu yolu destek kitaplığı ile buldum

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

Bu kitaplık olmadan başka bir çözüm var mı? Bunun gibi bir şey denedim, ancak her zaman harita başlatma ile ilgili bazı sorunlar var.

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

};

1
Bahsettiğiniz konular nelerdir? Bu haritayı başka bir global statik değişkenden / sabitten mi kullanmaya çalışıyorsunuz?
Péter Török

Bu ilişkilendirilebilir bir dizi değil string => int, bir int'i bir karaktere eşliyorsunuz. v = k + 'a' - 1.
Johnsyweb

Yanıtlar:


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

3
Basitlik için 1 kullanılarak elbette bir Boost.Assigntasarım gibi :) çok çok zarif
Matthieu M.

5
+1, teşekkürler. Not: İlklendirme satırını uygulama dosyama koymam gerekiyordu; bunu başlık dosyasında bırakmak, birden çok tanımdan dolayı bana hatalar verdi (başlatma kodu, başlık herhangi bir yere dahil edildiğinde çalışırdı).
System.Cats.Lol

1
İle g ++ v4.7.3, bu derler, ben eklemek kadar cout << A::myMap[1];içine main(). Hata veriyor. Niteleyicileri kaldırırsam hata oluşmaz const, bu yüzden haritaların en azından C ++ kitaplığının g ++ uygulamasında değil, operator[]a işleyemeyeceğini tahmin ediyorum const map.
Craig McQueen

2
Hata: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
Aslında, haritanın işleci [] sabit bir eşlemde çalışamaz çünkü bu işleç, eğer mevcut değilse başvurulan girdiyi oluşturur (çünkü eşlenen değere bir başvuru döndürdüğü için). C ++ 11, öğeye belirli bir anahtarla erişmenizi sağlayan, yoksa bir istisna atan at (KeyValT anahtarı) yöntemini tanıttı. ( en.cppreference.com/w/cpp/container/map/at ) Bu yöntem, const örneklerinde çalışır, ancak const olmayan bir örneğe bir öğe eklemek için kullanılamaz ([] operatörünün yaptığı gibi).
mbargiel

108

C ++ 11 standardı, derleyiciniz destekliyorsa bunu çok daha basit hale getiren tek tip başlatma getirmiştir:

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


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

Unordered_maps üzerinde Professional C ++ 'dan bu bölüme de bakın .


Cpp dosyasında eşittir işaretine ihtiyacımız var mı?
phoad

@phoad: Eşittir işareti gereksizdir.
Jinxed

Kullanımı gösterdiğiniz için teşekkür ederiz. Statik değişkenlerin nasıl değiştirileceğini anlamak gerçekten yardımcı oldu.
Kullanıcı9102d82

12

Yaptım! :)

C ++ 11 olmadan sorunsuz çalışır

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

11

boost::assign::map_list_ofYararlı bulursanız , ancak herhangi bir nedenle kullanamazsanız, kendinizinkini yazabilirsiniz :

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

Bu tür şeylerin, özellikle çok kısa olduklarında nasıl çalıştığını bilmek yararlıdır, ancak bu durumda bir işlev kullanırım:

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

6

Soruna farklı bir yaklaşım:

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
}

Yığından öbeğe tek tip kopya olmadığından bu daha verimlidir (yapıcı, tüm öğelerdeki yıkıcılar dahil). Bunun önemli olup olmadığı kullanım durumunuza bağlıdır. Dizelerle önemli değil! (ancak bu sürümü "daha temiz" bulabilir veya bulamayabilirsiniz)


3
RVO benimkindeki kopyalamayı ve Neil'in cevabını ortadan kaldırır.

6

Harita yalnızca derleme zamanında bilinen girişleri içerecekse ve haritanın anahtarları tamsayı ise, o zaman bir harita kullanmanıza gerek yoktur.

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

5
Bir haritaya ihtiyaç olmadığını belirten +1, ancak bunu yineleyemezsiniz
Viktor Sehr

4
Yine switchde bu korkunç. Neden olmasın return key + 'a' - 1?
Johnsyweb

12
@Hayalhanemersin Orijinal poster tarafından sağlanan haritanın yalnızca bir örnek olarak sunulduğunu ve sahip olduğu gerçek haritanın göstergesi olmadığını varsayıyorum. Bu nedenle, bunun return key + 'a' - 1gerçek haritalaması için işe yaramayacağını da varsayabilirim .
Matthew T. Staebler

3

Bunu deneyebilirsin:

Sınıfım.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

Sınıfım.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

Bu uygulama ile sınıflarınız sabit statik haritası özel bir üyedir ve genel bir get yöntemi kullanarak diğer sınıflar tarafından erişilebilir. Aksi takdirde, sabit olduğundan ve değiştirilemeyeceğinden, public get yöntemini kaldırabilir ve map değişkenini sınıflar public bölümüne taşıyabilirsiniz. Bununla birlikte, miras ve / veya çok biçimlilik gerekliyse createMap yöntemini özel veya korumalı olarak bırakırdım. İşte bazı kullanım örnekleri.

 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.

Orijinal gönderimi düzenledim, onun için derlediğim, oluşturduğum ve doğru şekilde çalıştırdığım orijinal kodda yanlış bir şey yoktu, sadece bir cevap olarak sunduğum ilk sürümüm, haritanın herkese açık olarak ilan edilmesi ve haritanın sabit ancak statik değildi.


2

Hala evrensel başlatmayı desteklemeyen bir derleyici kullanıyorsanız veya Boost'u kullanmak için rezervasyonunuz varsa, başka bir olası alternatif aşağıdaki gibi olacaktır.

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

0

Bir işlev çağrısı sabit bir ifadede görünemez.

bunu deneyin: (sadece bir örnek)

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

6
Bir işlev, bir const nesnesini başlatmak için kesinlikle kullanılabilir.

OP'nin kodu static map<int,int> myMap = create_map();yanlış.
Prasoon Saurav

3
Sorudaki kod yanlıştır, hepimiz aynı fikirdeyiz, ancak bu yanıtta söylediğiniz gibi 'sabit ifadelerle' hiçbir ilgisi yoktur, bunun yerine bir sınıfın yalnızca sabit statik üyelerini tamsayı veya enum türünde ise bildirim. Diğer tüm türler için, başlatma, bildirimde değil üye tanımında yapılmalıdır.
David Rodríguez - dribeas

Neil'in cevabı g ++ ile derlenir. Yine de, GNU araç zincirinin önceki sürümlerinde bu yaklaşımla ilgili bazı problemler yaşadığımı hatırlıyorum. Evrensel bir doğru cevap var mı?
Basilevs

1
@Prasoon: Derleyicinin ne dediğini bilmiyorum, ancak soru kodundaki hata, başlatmanın sabit bir ifade olup olmadığına bakılmaksızın, sınıf bildiriminde sınıf türünün sabit bir üye özniteliğini başlatıyor. Bir sınıf tanımlarsanız: struct testdata { testdata(int){} }; struct test { static const testdata td = 5; }; testdata test::td;başlatma sabit bir ifade ( 5) ile gerçekleştirilse bile derleme başarısız olur . Yani, 'sabit ifade' ilk kodun doğruluğu (veya eksikliği) ile ilgisizdir.
David Rodríguez - dribeas

-2

Sık sık bu kalıbı kullanıyorum ve sizin de kullanmanızı tavsiye ediyorum:

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;

Elbette çok okunabilir değil, ancak diğer kitaplıklar olmadan yapabileceğimiz en iyi şey bu. Ayrıca girişiminizdeki gibi bir haritadan diğerine kopyalamak gibi gereksiz işlemler olmayacaktır.

Bu, işlevlerin içinde daha da kullanışlıdır: Bunun yerine:

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

Aşağıdakileri kullanın:

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

Artık boole değişkeniyle uğraşmak için burada ihtiyacınız yok, aynı zamanda işlev içindeki statik değişkenin başlatıcısı zaten çağrıldıysa, kontrol edilen gizli global değişkene sahip olmayacaksınız.


6
Kalıtım, ilk değil, son çare olmalıdır.

RVO'yu destekleyen bir derleyici, işlev sürümleriyle gereksiz kopyalamayı ortadan kaldırır. C ++ 0x hareket semantiği, kullanılabilir olduklarında geri kalanını ortadan kaldırır. Her durumda, bunun bir darboğaza yakın olduğundan şüpheliyim.

Roger, RVO, && ve hareket semantiğinin çok iyi farkındayım. Bu, şimdilik minimum miktarda kod ve varlıkta bir çözümdür. Ayrıca, fonksiyonların içindeki fonksiyonları tanımlamamıza izin verilmediğinden, tüm C ++ 0x özellikleri, fonksiyon örneği içindeki statik nesneye yardımcı olmaz.
Pavel Chikulaev
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.