Как инициализировать частную статическую карту констант в C ++? - PullRequest
92 голосов
/ 14 апреля 2010

Мне нужен только словарь или ассоциативный массив string => int.

Для этого случая есть карта типов C ++.

Но мне нужен только один экземпляр карты (-> static), и эту карту нельзя изменить (-> 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();

};

Ответы [ 10 ]

99 голосов
/ 14 апреля 2010
#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() {
}
88 голосов
/ 31 декабря 2011

Стандарт 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}
};

См. Также этот раздел из Professional C ++ , на unordered_maps.

12 голосов
/ 14 августа 2013

Я сделал это! :)

Отлично работает без 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 голосов
/ 14 апреля 2010

Если вы найдете 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 голосов
/ 14 апреля 2010

Если карта должна содержать только записи, которые известны во время компиляции, а ключи к карте являются целыми числами, тогда вам вообще не нужно использовать карту.

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
    }
}
6 голосов
/ 14 апреля 2010

Другой подход к проблеме:

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
}

Это более эффективно, так как нет единой копии из стека в кучу (включая конструктор, деструкторы для всех элементов) Имеет ли это значение или нет, зависит от вашего варианта использования. Не имеет значения со строками! (но вы можете или не можете найти эту версию "чище")

2 голосов
/ 21 июля 2013

Если вы используете компилятор, который все еще не поддерживает универсальную инициализацию, или у вас есть резервирование в использовании 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;
}();
1 голос
/ 17 марта 2015

Вы можете попробовать это:

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

С этой реализацией ваша статическая константа классов является частным членом и может быть доступна другим классам с помощью общедоступного метода get. Иначе поскольку он постоянен и не может измениться, вы можете удалить публичный метод 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.

Я отредактировал свой исходный пост, в исходном коде, в котором я разместил его, не было ничего плохого, он был скомпилирован, скомпилирован и запущен правильно. В моей первой версии, которую я представил в качестве ответа, карта была объявлена ​​общедоступной, и карта была постоянной, но не статичной.

0 голосов
/ 14 апреля 2010

Я часто использую этот шаблон и рекомендую использовать его также:

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

Мало того, что вам больше здесь не нужно иметь дело с логической переменной, у вас не будет скрытой глобальной переменной, которая проверяется, если инициализатор статической переменной внутри функции уже был вызван.

0 голосов
/ 14 апреля 2010

Вызов функции не может появляться в константном выражении.

попробуйте это: (просто пример)

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