C ++에서 정적 std :: map <int, int> 초기화


449

정적지도를 초기화하는 올바른 방법은 무엇입니까? 초기화 할 정적 함수가 필요합니까?

답변:


620

C ++ 11 사용하기 :

#include <map>
using namespace std;

map<int, char> m = {{1, 'a'}, {3, 'b'}, {5, 'c'}, {7, 'd'}};

Boost.Assign 사용 :

#include <map>
#include "boost/assign.hpp"
using namespace std;
using namespace boost::assign;

map<int, char> m = map_list_of (1, 'a') (3, 'b') (5, 'c') (7, 'd');

115
C ++로 수행 한 것과 같은 것을 볼 때마다 뒤에있는 끔찍한 템플릿 코드를 생각합니다. 좋은 예입니다!
Greg Hewgill

34
이러한 유틸리티를 구현하는 모든 끔찍한 템플릿 코드의 장점은 라이브러리에 깔끔하게 캡슐화되어 있으며 최종 사용자가 복잡성을 처리 할 필요가 거의 없다는 것입니다.
Steve Guidi

45
@QBziZ : 귀사가 "충분히 표준"이 아니라는 이유로 부스트 사용을 거부하면 C ++ 라이브러리 "충분히 표준"인 것이 무엇인지 궁금합니다 . Boost는 C ++ 코더 표준 동반자입니다.
DevSolar

47
Boost (여기서 다른 곳)에서 내 문제는 종종 그것없이 (이 경우 C ++ 11 또는 C ++ 11 이전에 ) 함수를 사용할 수 있다는 것 입니다. Boost는 상당한 컴파일 시간 오버 헤드를 추가하고, 저장소에 보관할 파일이 많았으며 (아카이브를 만드는 경우 주위를 복사 / zip / 추출해야 함) 그것이 내가 그것을 사용하지 않으려는 이유입니다. 포함 / 포함하지 않을 파일을 선택할 수 있지만 일반적으로 Boost의 상호 종속성에 대해 걱정할 필요가 없으므로 전체를 복사하면됩니다.
bobobobo 2016 년

7
Boost의 내 문제는 종종 몇 가지 새로운 라이브러리 종속성이 있다는 것입니다. 일반적으로 올바르게 작동하려면 더 많은 패키지를 설치해야합니다. 우리는 이미 libstdc ++가 필요합니다. 예를 들어, Boost ASIO 라이브러리에는 설치해야 할 최소 2 개의 새 라이브러리 (아마도 더 많음)가 필요합니다. C ++ 11 / 14는 Boost가 필요 없을 때 훨씬 쉬워졌습니다.
Rahly

135

가장 좋은 방법은 함수를 사용하는 것입니다.

#include <map>

using namespace std;

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

map<int,int> m = create_map();

18
이것이 왜 '최고'입니까? 예를 들어 @Dreamer의 답변보다 나은 이유는 무엇입니까?
Lorne의 후작

6
정말 간단하고 기존의 다른 구조 (예 : Boost :: Assign 또는 다시 구현)에 의존하지 않기 때문에 "최고"라고 생각합니다. 그리고
@Dreamer

3
여기에 위험이 있습니다 . 컴파일러가 선언 만 보았지만 실제 변수 정의를 아직 실행하지 않은 경우extern 변수는이 "주 런타임 생성자 이전"에 올바른 값을 갖지 않습니다 . extern
bobobobo 2016 년

5
아니, 위험은 정적 변수가 어떤 순서로 초기화되어야하는지 (적어도 컴파일 단위에서) 말해야 할 것이 없다는 것입니다. 그러나 이것은이 질문과 관련된 문제가 아닙니다. 이것은 정적 변수의 일반적인 문제입니다.
PierreBdR

5
부스트 없음 및 C ++ 11 => +1 없음. 이 함수를 사용 const map<int,int> m = create_map()하여 초기화 목록에서 클래스의 const 멤버 를 초기화 할 수 있습니다 .struct MyClass {const map<int, int> m; MyClass(); }; MyClass::MyClass() : m(create_map())
ribamar

115

부스트와 비슷한 것을 만드는 것은 복잡한 문제가 아닙니다. 다음은 부스트가 수행 한 작업을 거의 재현하는 생성자를 포함하여 함수가 3 개 뿐인 클래스입니다.

template <typename T, typename U>
class create_map
{
private:
    std::map<T, U> m_map;
public:
    create_map(const T& key, const U& val)
    {
        m_map[key] = val;
    }

    create_map<T, U>& operator()(const T& key, const U& val)
    {
        m_map[key] = val;
        return *this;
    }

    operator std::map<T, U>()
    {
        return m_map;
    }
};

용법:

std :: map mymap = create_map <int, int> (1,2) (3,4) (5,6);

위의 코드는 전역 변수 또는 클래스의 정적 멤버를 초기화하는 데 가장 적합하며 초기화 해야하는 시점을 모르지만 값을 사용할 수 있는지 확인하려고합니다.

말하자면, 기존 std :: map에 요소를 삽입해야합니다 ... 여기에 다른 클래스가 있습니다.

template <typename MapType>
class map_add_values {
private:
    MapType mMap;
public:
    typedef typename MapType::key_type KeyType;
    typedef typename MapType::mapped_type MappedType;

    map_add_values(const KeyType& key, const MappedType& val)
    {
        mMap[key] = val;
    }

    map_add_values& operator()(const KeyType& key, const MappedType& val) {
        mMap[key] = val;
        return *this;
    }

    void to (MapType& map) {
        map.insert(mMap.begin(), mMap.end());
    }
};

용법:

typedef std::map<int, int> Int2IntMap;
Int2IntMap testMap;
map_add_values<Int2IntMap>(1,2)(3,4)(5,6).to(testMap);

여기에서 GCC 4.7.2와 함께 작동하는 것을 참조하십시오 : http://ideone.com/3uYJiH

############### 아래의 모든 내용은 폐물입니다 ################

편집 : map_add_values내가 제안한 원래 솔루션 인 아래 클래스는 GCC 4.5 이상에서 실패합니다. 기존지도 값을 추가 하는 방법은 위의 코드를 참조 하십시오 .


template<typename T, typename U>
class map_add_values
{
private:
    std::map<T,U>& m_map;
public:
    map_add_values(std::map<T, U>& _map):m_map(_map){}
    map_add_values& operator()(const T& _key, const U& _val)
    {
        m_map[key] = val;
        return *this;
    }
};

용법:

std :: map <int, int> my_map;
// 나중에 코드를 따라
map_add_values ​​<int, int> (my_map) (1,2) (3,4) (5,6);

참고 : 이전 operator []에는 실제 값을 추가하는 데 사용했습니다 . dalle의 의견으로는 불가능합니다.

#################### 폐기 섹션의 끝 ####################


3
첫 번째 샘플을 <int, string>으로 사용하여 열거 형의 오류 번호를 메시지와 바인딩합니다. 매력처럼 작동합니다. 감사합니다.
slashmais

1
operator[]하나의 인수 만 취합니다.
dalle

1
@ 달 : 좋은 캐치! 어떤 이유로 나는 과부하 된 [] 연산자가 더 많은 것을 받아 들일 수 있다고 생각했습니다.
Vite Falcon

2
이것은 환상적인 답변입니다. OP가 하나를 선택하지 않은 것은 부끄러운 일입니다. 메가 소품이 필요합니다.
Thomas Thorogood

gcc에서는 map_add_values가 작동하지 않습니다. error: conflicting declaration ‘map_add_values<int, int> my_map’ error: ‘my_map’ has a previous declaration as ‘std::map<int, int> my_map’
Martin Wang

42

다음은 2 요소 데이터 생성자를 사용하는 다른 방법입니다. 초기화에 필요한 기능이 없습니다. 타사 코드 (부스트), 정적 함수 또는 객체, 트릭 없음, 간단한 C ++이 없습니다.

#include <map>
#include <string>

typedef std::map<std::string, int> MyMap;

const MyMap::value_type rawData[] = {
   MyMap::value_type("hello", 42),
   MyMap::value_type("world", 88),
};
const int numElems = sizeof rawData / sizeof rawData[0];
MyMap myMap(rawData, rawData + numElems);

이 답변을 쓴 이후 C ++ 11이 나왔습니다. 새로운 초기화 목록 기능을 사용하여 STL 컨테이너를 직접 초기화 할 수 있습니다.

const MyMap myMap = { {"hello", 42}, {"world", 88} };

25

예를 들면 다음과 같습니다.

const std::map<LogLevel, const char*> g_log_levels_dsc =
{
    { LogLevel::Disabled, "[---]" },
    { LogLevel::Info,     "[inf]" },
    { LogLevel::Warning,  "[wrn]" },
    { LogLevel::Error,    "[err]" },
    { LogLevel::Debug,    "[dbg]" }
};

map이 클래스의 데이터 멤버 인 경우 C ++ 17부터 다음 방법으로 헤더에서 직접 초기화 할 수 있습니다.

// Example

template<>
class StringConverter<CacheMode> final
{
public:
    static auto convert(CacheMode mode) -> const std::string&
    {
        // validate...
        return s_modes.at(mode);
    }

private:
    static inline const std::map<CacheMode, std::string> s_modes =
        {
            { CacheMode::All, "All" },
            { CacheMode::Selective, "Selective" },
            { CacheMode::None, "None" }
            // etc
        };
}; 

24

정적 객체 안에 맵을 래핑하고 맵 초기화 코드를이 오브젝트의 생성자에 넣습니다. 이렇게하면 초기화 코드가 실행되기 전에 맵이 생성됩니다.


1
나는 이것에 당신과 함께 있습니다. 그것은 또한 조금 더 빠릅니다 :)
QBziZ

2
무엇보다 빠르지? 이니셜 라이저를 사용하는 전역 정적? 아닙니다. (RVO를 기억하십시오).
Pavel Minaev

7
좋은 대답입니다. 실제 예제 코드를 보면 기쁠 것입니다.
Sungguk Lim

18

순수한 C ++ 98 해결 방법을 공유하고 싶었습니다.

#include <map>

std::map<std::string, std::string> aka;

struct akaInit
{
    akaInit()
    {
        aka[ "George" ] = "John";
        aka[ "Joe" ] = "Al";
        aka[ "Phil" ] = "Sue";
        aka[ "Smitty" ] = "Yando";
    }
} AkaInit;

2
방법은 이럴 선호한다 삽입이, 기본 생성자없이 객체에 대한 작업을하지 않습니다
알레산드로 TERUZZI

16

당신은 시도 할 수 있습니다:

std::map <int, int> mymap = 
{
        std::pair <int, int> (1, 1),
        std::pair <int, int> (2, 2),
        std::pair <int, int> (2, 2)
};

1
당신은 당신이뿐만 아니라 짧은 구문을 사용할 수있는 경우 11 ++ C 전에 비 집계 유형과 목록 초기화 사용할 수 없습니다 {1, 2}대신 std::pair<int, int>(1, 2).
Ferruccio

9

PierreBdR지도를 복사하지 않고 와 비슷합니다 .

#include <map>

using namespace std;

bool create_map(map<int,int> &m)
{
  m[1] = 2;
  m[3] = 4;
  m[5] = 6;
  return true;
}

static map<int,int> m;
static bool _dummy = create_map (m);

12
어쨌든 복사되지 않았을 것입니다.
GManNickG

2
그러나이 방법으로 map은 정적 const 일 수 없었습니다.
xmoex

6

C ++ 98에 갇혀 있고 부스트를 사용하지 않으려는 경우 정적 맵을 초기화해야 할 때 사용하는 솔루션이 있습니다.

typedef std::pair< int, char > elemPair_t;
elemPair_t elemPairs[] = 
{
    elemPair_t( 1, 'a'), 
    elemPair_t( 3, 'b' ), 
    elemPair_t( 5, 'c' ), 
    elemPair_t( 7, 'd' )
};

const std::map< int, char > myMap( &elemPairs[ 0 ], &elemPairs[ sizeof( elemPairs ) / sizeof( elemPairs[ 0 ] ) ] );

-4

여기에 아주 좋은 답변이 있지만, 나에게있어, "당신이 아는 모든 것이 망치 일 때"의 경우처럼 보입니다 ...

정적지도를 초기화하는 표준 방법이없는 이유에 대한 가장 간단한 대답은 정적지도를 사용해야 할 이유가 없다는 것입니다.

지도는 알려지지 않은 요소 집합을 빠르게 조회하도록 설계된 구조입니다. 미리 요소를 알고 있다면 간단히 C- 어레이를 사용하십시오. 정렬 된 방식으로 값을 입력하거나이를 수행 할 수없는 경우 정렬을 실행하십시오. 그런 다음 stl :: functions를 사용하여 항목 lower_bound / upper_bound를 루프 업하여 log (n) 성능을 얻을 수 있습니다. 내가 이전에 이것을 테스트했을 때, 그들은 보통지도보다 적어도 4 배 더 빨리 수행합니다.

장점은 여러 가지입니다 ...-더 빠른 성능 (* 4, 많은 CPU 유형에서 측정했으며 항상 약 4입니다)-더 간단한 디버깅. 선형 레이아웃으로 무슨 일이 일어나고 있는지 쉽게 알 수 있습니다. -필요한 복사 작업의 간단한 구현. -런타임에 메모리를 할당하지 않으므로 예외가 발생하지 않습니다. -표준 인터페이스이므로 DLL, 언어 등을 쉽게 공유 할 수 있습니다.

계속할 수는 있지만 더 많은 것을 원한다면 Stroustrup의 많은 블로그를 주제로 살펴보십시오.


8
성능이 맵을 사용하는 유일한 이유는 아닙니다. 예를 들어, 값을 함께 연결하려는 경우가 많으며 (예 : 오류 메시지가있는 오류 코드) 맵에서 사용 및 액세스가 비교적 간단합니다. 그러나이 블로그 항목에 대한 링크가 흥미로울 수 있습니다. 아마 내가 잘못하고 있습니다.
MatthiasB

5
배열을 사용하는 것이 훨씬 쉽고 성능이 높습니다. 그러나 인덱스 (키)가 인접하지 않고 넓게 이격되어 있으면 맵이 필요합니다.
KarlU

1
A map는 부분 함수 (수학적 의미의 함수이지만 프로그래밍 의미의 함수)를 나타내는 유용한 형식이기도합니다. 배열은 그렇게하지 않습니다. 예를 들어 문자열을 사용하여 배열에서 데이터를 조회 할 수 없습니다.
einpoklum

3
귀하의 답변은 유효한 질문에 대한 답변을 시도하지 않고 언어의 한계에 대해 추측하고 다른 문제에 대한 해결책을 제안하므로 공감대입니다. 실제 시나리오-라이브러리 오류 코드를 텍스트 문자열에 매핑 (연속 또는 비 연속) 배열에서 검색 시간은 O (n)이며 O (log (n))에 정적 매핑하여 향상시킬 수 있습니다.
Tosha

2
실제로 "정적 맵을 사용해야 할 이유가 없다 ..."라면 사용하기 쉬운 구문 (초기화 자 목록)이 C ++ 11에 추가 된 것은 매우 이상합니다.
ellisbben
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.