Як ініціалізувати приватну статичну карту const у C ++?


108

Мені потрібен лише словник чи асоціативний масив string=> int.

Для цього випадку існує карта типу C ++.

Але мені потрібні лише один екземпляр карти для всіх (-> статичний), і цю карту неможливо змінити (-> const);

Я знайшов цей спосіб за допомогою збільшити бібліотеку

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

Чи є інше рішення без цієї ліб? Я спробував щось подібне, але завжди є проблеми з ініціалізацією карти.

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
На які питання ви звертаєтесь? Ви намагаєтесь використовувати цю карту з іншої глобальної статичної змінної / константи?
Петер Тьорьк

Це не асоціативний рядок масиву => int, ви відображаєте int до символу. v = k + 'a' - 1.
Johnsyweb

Відповіді:


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
+1 для простоти, звичайно, використання Boost.Assignподібного дизайну теж дуже акуратно :)
Матьє М.

5
+1, спасибі Примітка: мені довелося помістити рядок ініціалізації у свій файл реалізації; Залишаючи його у файлі заголовка, мені давались помилки через декілька визначень (код ініціалізації запускався б, коли заголовок десь був включений).
System.Cats.Lol

1
З г ++ v4.7.3, компілюється, поки я не додати cout << A::myMap[1];в main(). Це дає помилку. Помилка не виникає, якщо я видаляю constкласифікатори, тому, мабуть, карта не operator[]може впоратися з const map, принаймні, не в г ++ реалізації бібліотеки C ++.
Крейг МакКуін

2
Помилка: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]
Крейг МакКуїн

4
Дійсно, оператор карти [] не може працювати на карті const, тому що цей оператор створює посилання, якщо його немає (оскільки він повертає посилання на відображене значення). C ++ 11 представив метод at (KeyValT key), який дозволяє отримувати доступ до елемента за допомогою заданого ключа, викидаючи виняток, якщо його не існує. ( en.cppreference.com/w/cpp/container/map/at ) Цей метод буде працювати в екземплярах const, але не може бути використаний для вставки елемента в екземпляр non-const (як це робиться в оператора []).
mbargiel

108

Стандарт C ++ 11 представив єдину ініціалізацію, що робить це набагато простіше, якщо ваш компілятор підтримує його:

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


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

Дивіться також цей розділ у розділі Професійний C ++ , на unororder_maps.


Чи потрібен нам знак рівності у файлі cpp?
фоад

@phoad: Знак рівності зайвий.
Jinxed

Дякуємо, що показали використання. Це було дуже корисно зрозуміти, як змінити статичні змінні.
User9102d82

12

Я зробив це! :)

Відмінно працює без 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);

11

Якщо ви вважаєте boost::assign::map_list_ofкорисним, але не можете його використовувати з якихось причин, можете написати свій власний :

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

Корисно знати, як працюють такі речі, особливо коли вони такі короткі, але в цьому випадку я використовую функцію:

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

Інший підхід до проблеми:

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
}

Це ефективніше, оскільки немає однотипної копії з стека в купу (включаючи конструктор, деструктори на всіх елементах). Чи це має значення чи ні, залежить від Вашого випадку використання. Не має значення струн! (але ви можете чи не можете знайти цю версію "чистішою")


3
RVO усуває копіювання у моїй та Нілівській відповіді.

6

Якщо карта повинна містити лише записи, які відомі під час компіляції, а ключі на карті - цілі числа, то вам зовсім не потрібно використовувати карту.

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
+1 за вказівку на те, що карта не потрібна, однак ви не можете її повторити
Віктор

4
Це switchжахливо, хоча. Чому ні return key + 'a' - 1?
Johnsyweb

12
@Johnsyweb. Я припускаю, що відображення оригінального плаката відображалось лише як приклад, а не вказує на фактичне відображення у нього. Тому я б припустив, що return key + 'a' - 1це не спрацювало б для його фактичного відображення.
Меттью Т. Стаеблер

3

Ви можете спробувати це:

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

При цій реалізації постійні статичні карти ваших класів є приватним членом і можуть бути доступні для інших класів за допомогою методу public get. В іншому випадку, оскільки він є постійним і не може змінитися, ви можете видалити метод загальнодоступного отримання та перемістити змінну карти в публічний розділ класів. Однак я б залишив метод createMap приватним або захищеним, якщо потрібне успадкування та поліморфізм. Ось кілька зразків використання.

 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.

Я відредагував свій оригінальний пост, нічого оригінального коду, в якому я розмістив для нього, склав, склав і запустив правильно, було саме те, що моя перша версія, яку я представив як відповідь, карта була оголошена публічною, а карта - const, але не був статичним.


2

Якщо ви використовуєте компілятор, який все ще не підтримує універсальну ініціалізацію, або ви зарезервували використання Boost, іншою можливою альтернативою буде наступна

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

Виклик функції не може відображатися в постійному виразі.

спробуйте це: (лише приклад)

#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
Функція, безумовно, може бути використана для ініціалізації об'єкта const.

У коді ОП static map<int,int> myMap = create_map();невірно.
Prasoon Saurav

3
Код у питанні невірний, ми всі погоджуємось на це, але це не має нічого спільного з "постійними виразами", як ви говорите в цій відповіді, а швидше з тим, що ви можете ініціалізувати лише постійних статичних членів класу в декларація, якщо вони мають цілий чи перерахунковий тип. Для всіх інших типів ініціалізація повинна проводитися у визначенні члена, а не в декларації.
David Rodríguez - dribeas

Відповідь Ніла компілюється з g ++. Тим не менш, я пам’ятаю, що були проблеми з таким підходом у попередніх версіях інструментальної мережі GNU. Чи є універсальна правильна відповідь?
Basilevs

1
@Prasoon: Я не знаю, що говорить компілятор, але помилка в коді питання - ініціалізація атрибута постійного члена типу класу в декларації класу, незалежно від того, ініціалізація є постійним виразом чи ні. Якщо ви визначите клас: struct testdata { testdata(int){} }; struct test { static const testdata td = 5; }; testdata test::td;він не зможе скластись, навіть якщо ініціалізація виконується з постійним виразом ( 5). Тобто, «постійне вираження» не має значення для правильності (або його відсутності) початкового коду.
Девід Родрігес - дрибес

-2

Я часто використовую цю схему і рекомендую вам її також використовувати:

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;

Впевнені, що це не дуже читабельно, але найкраще, що ми можемо зробити без інших лайків. Також не буде жодних зайвих операцій, таких як копіювання з однієї карти на іншу, як у вашій спробі.

Це ще корисніше всередині функцій: Замість:

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

Використовуйте наступне:

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

Тут вам не потрібно більше мати справу з булевою змінною, у вас не буде прихованої глобальної змінної, яка перевіряється, якщо ініціалізатор статичної змінної всередині функції вже викликався.


6
Спадщина має бути інструментом останньої інстанції, а не першим.

Компілятор, який підтримує RVO, виключає зайве копіювання з версіями функцій. Семантика переміщення C ++ 0x усуває решту, коли вони будуть доступні. У будь-якому випадку, я сумніваюся, це близько до того, що це вузьке місце.

Роджере, я добре знаю RVO, && та семантику руху. На сьогодні це рішення в мінімальній кількості коду та сутності. Крім того, всі функції C ++ 0x не допоможуть статичному об'єкту в прикладі функції, оскільки нам не дозволяється визначати функції всередині функцій.
Павло Чикулаєв
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.