C ++에서 비공개 정적 const 맵을 초기화하는 방법은 무엇입니까?


108

사전 또는 연관 배열 string=> 만 필요합니다 int.

이 경우 유형 맵 C ++가 있습니다.

하지만 모든 인스턴스 (-> static)에 대해 하나의 맵만 필요하며이 맵은 변경할 수 없습니다 (-> const);

나는 부스트 라이브러리로 이런 방법을 찾았습니다.

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

이 lib가없는 다른 솔루션이 있습니까? 나는 이와 같은 것을 시도했지만 항상 맵 초기화에 몇 가지 문제가 있습니다.

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
당신이 언급하는 문제는 무엇입니까? 다른 전역 정적 변수 / 상수에서이 맵을 사용하려고합니까?
Péter Török

그것은 연관 배열 문자열 => int가 아니며 int를 char에 매핑하고 있습니다. 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설계 등 : 너무 멋지다
마티유 M.

5
+1, 감사합니다. 참고 : 구현 파일에 초기화 줄을 넣어야했습니다. 헤더 파일에 남겨두면 여러 정의로 인해 오류가 발생했습니다 (헤더가 어딘가에 포함될 때마다 초기화 코드가 실행 됨).
System.Cats.Lol

1
와 g ++ v4.7.3,이 컴파일, 내가 추가 할 때까지 cout << A::myMap[1];main(). 오류가 발생합니다. const한정자를 제거하면 오류가 발생하지 않으므로 지도가 C ++ 라이브러리의 g ++ 구현이 아닌 적어도를 operator[]처리 할 수 ​​없다고 생각 const map합니다.
Craig McQueen

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
실제로 맵의 operator []는 const 맵에서 작동 할 수 없습니다. 해당 연산자가 존재하지 않는 경우 참조 된 항목을 생성하기 때문입니다 (매핑 된 값에 대한 참조를 반환하기 때문에). C ++ 11에서는 지정된 키로 항목에 액세스 할 수있는 at (KeyValT key) 메서드를 도입하여 존재하지 않는 경우 예외를 throw합니다. ( en.cppreference.com/w/cpp/container/map/at )이 메서드는 const 인스턴스에서 작동하지만 상수 가 아닌 인스턴스에 요소를 삽입하는 데 사용할 수 없습니다 ([] 연산자처럼).
mbargiel 2014

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

무순 맵에 대한 Professional C ++의이 섹션을 참조하십시오 .


cpp 파일에 등호가 필요합니까?
phoad

@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는 내 복사와 Neil의 대답을 제거합니다.

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, 그러나 이것을 반복 할 수는 없습니다
Viktor Sehr

4
switch비록 끔찍한입니다. 왜 안돼 return key + 'a' - 1?
Johnsyweb

12
안녕하세요. 나는 원래 포스터에서 제공 한 매핑이 그가 가지고있는 실제 매핑을 나타내지 않고 단지 예로서 만 제시되었다고 가정합니다. 따라서 return key + 'a' - 1실제 매핑에는 작동하지 않을 것이라고 가정합니다 .
Matthew T. Staebler

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

이 구현을 통해 클래스 상수 정적 맵은 개인 멤버이며 공용 get 메소드를 사용하여 다른 클래스에 액세스 할 수 있습니다. 그렇지 않으면 상수이고 변경할 수 없으므로 public get 메소드를 제거하고 map 변수를 classes public 섹션으로 이동할 수 있습니다. 그러나 상속 및 / 또는 다형성이 필요한 경우 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 객체를 초기화하는 데 사용할 수 있습니다.

OP의 코드 static map<int,int> myMap = create_map();가 올바르지 않습니다.
Prasoon Saurav

3
질문의 코드가 잘못되었습니다. 우리 모두 동의합니다. 그러나이 답변에서 말한 것처럼 '상수 표현식'과는 아무 관련이 없습니다. 오히려 클래스의 상수 정적 멤버 만 초기화 할 수 있다는 사실과 관련이 있습니다. 정수 또는 열거 형인 경우 선언. 다른 모든 유형의 경우 선언이 아닌 멤버 정의에서 초기화를 수행해야합니다.
David Rodríguez-dribeas

Neil의 답변은 g ++로 컴파일됩니다. 그래도 이전 버전의 GNU 도구 모음에서이 접근 방식에 몇 가지 문제가 있었던 것을 기억합니다. 보편적 인 정답이 있습니까?
Basilevs

1
@Prasoon : 컴파일러가 무엇을 말하는지 모르겠지만 질문 코드의 오류는 초기화가 상수 표현식인지 여부에 관계없이 클래스 선언에서 클래스 유형의 상수 멤버 속성을 초기화하는 것입니다. 클래스를 정의하는 경우 : struct testdata { testdata(int){} }; struct test { static const testdata td = 5; }; testdata test::td;상수 표현식 ( 5)으로 초기화를 수행하더라도 컴파일에 실패합니다 . 즉, '상수 표현'은 초기 코드의 정확성 (또는 부족)과는 무관합니다.
David Rodríguez-dribeas

-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 이동 의미론은 사용 가능하면 나머지를 제거합니다. 어쨌든 병목 현상에 가까워진 것 같지 않다.

Roger, 저는 RVO, && 및 이동 의미론을 잘 알고 있습니다. 이것은 최소한의 코드와 엔터티를위한 솔루션입니다. 또한 모든 C ++ 0x 기능은 함수 내부에서 함수를 정의 할 수 없기 때문에 함수 예제 내부의 정적 개체에 도움이되지 않습니다.
Pavel Chikulaev 2010
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.