키가 존재하지 않을 때 기본값 std::map
의 operator[]
반환 을 지정하는 방법 이 있습니까?
답변:
아니, 없습니다. 가장 간단한 해결책은이 작업을 수행하기위한 무료 템플릿 함수를 작성하는 것입니다. 다음과 같은 것 :
#include <string>
#include <map>
using namespace std;
template <typename K, typename V>
V GetWithDef(const std::map <K,V> & m, const K & key, const V & defval ) {
typename std::map<K,V>::const_iterator it = m.find( key );
if ( it == m.end() ) {
return defval;
}
else {
return it->second;
}
}
int main() {
map <string,int> x;
...
int i = GetWithDef( x, string("foo"), 42 );
}
C ++ 11 업데이트
목적 : 일반적인 연관 컨테이너와 선택적 비교기 및 할당 자 매개 변수를 설명합니다.
template <template<class,class,class...> class C, typename K, typename V, typename... Args>
V GetWithDef(const C<K,V,Args...>& m, K const& key, const V & defval)
{
typename C<K,V,Args...>::const_iterator it = m.find( key );
if (it == m.end())
return defval;
return it->second;
}
operator[]
기본값과 똑같은 동작을 제공하려면 기본값을 if ( it == m.end() )
블록 내부의 맵에 삽입해야합니다.
이것이 질문에 정확히 대답하지는 않지만 다음과 같은 코드로 문제를 우회했습니다.
struct IntDefaultedToMinusOne
{
int i = -1;
};
std::map<std::string, IntDefaultedToMinusOne > mymap;
C ++ 표준 (23.3.1.2)은 새로 삽입 된 값이 기본적으로 생성되도록 지정하므로 map
자체적으로이를 수행하는 방법을 제공하지 않습니다. 선택 사항은 다음과 같습니다.
operator[]
해당 기본값을 삽입하도록 구현하는 자체 클래스로 맵을 래핑합니다 .보다 일반적인 버전, C ++ 98 / 03 및 기타 컨테이너 지원
일반 연관 컨테이너와 함께 작동하며 유일한 템플릿 매개 변수는 컨테이너 유형 자체입니다.
지원되는 용기 std::map
, std::multimap
, std::unordered_map
, std::unordered_multimap
, wxHashMap
, QMap
, QMultiMap
, QHash
, QMultiHash
, 등
template<typename MAP>
const typename MAP::mapped_type& get_with_default(const MAP& m,
const typename MAP::key_type& key,
const typename MAP::mapped_type& defval)
{
typename MAP::const_iterator it = m.find(key);
if (it == m.end())
return defval;
return it->second;
}
용법:
std::map<int, std::string> t;
t[1] = "one";
string s = get_with_default(t, 2, "unknown");
다음은 Python get()
의 dict
유형 메소드와 더 유사한 래퍼 클래스를 사용하여 유사한 구현입니다 . https://github.com/hltj/wxMEdit/blob/master/src/xm/xm_utils.hpp
template<typename MAP>
struct map_wrapper
{
typedef typename MAP::key_type K;
typedef typename MAP::mapped_type V;
typedef typename MAP::const_iterator CIT;
map_wrapper(const MAP& m) :m_map(m) {}
const V& get(const K& key, const V& default_val) const
{
CIT it = m_map.find(key);
if (it == m_map.end())
return default_val;
return it->second;
}
private:
const MAP& m_map;
};
template<typename MAP>
map_wrapper<MAP> wrap_map(const MAP& m)
{
return map_wrapper<MAP>(m);
}
용법:
std::map<int, std::string> t;
t[1] = "one";
string s = wrap_map(t).get(2, "unknown");
C ++ 17은 try_emplace
정확히이 기능 을 제공합니다 . 값 생성자에 대한 키와 인수 목록을 가져 와서 an iterator
과 a 쌍을 반환 합니다 bool
. : http://en.cppreference.com/w/cpp/container/map/try_emplace
기본값을 지정하는 방법은 없습니다. 항상 기본값 (제로 매개 변수 생성자)에 의해 구성된 값입니다.
실제로 operator[]
맵에 주어진 키에 대한 값이 존재하지 않는 것처럼 기본 생성자의 값으로 새 키를 삽입하는 것처럼 예상보다 더 많은 작업을 수행 할 수 있습니다.
find
. 주어진 키에 대한 요소가 없으면 끝 반복자를 반환 하는 새 항목을 추가하지 않으려면 사용할 수 있습니다 .
find
입니까?
template<typename T, T X>
struct Default {
Default () : val(T(X)) {}
Default (T const & val) : val(val) {}
operator T & () { return val; }
operator T const & () const { return val; }
T val;
};
<...>
std::map<KeyType, Default<ValueType, DefaultValue> > mapping;
다른 답변에서 말했듯이 값은 기본 생성자를 사용하여 초기화됩니다. 그러나 단순 유형 (int, float, pointer 또는 POD (plan old data) 유형과 같은 통합 유형)의 경우 값이 0으로 초기화됩니다 (또는 값 초기화에 의해 0으로 처리된다는 점을 추가하는 것이 유용합니다. 같은 것), 사용되는 C ++ 버전에 따라 다름).
어쨌든 결론은 단순한 유형의 맵이 새 항목을 자동으로 0으로 초기화한다는 것입니다. 따라서 어떤 경우에는 기본 초기 값을 명시 적으로 지정하는 것에 대해 걱정할 필요가 없습니다.
std::map<int, char*> map;
typedef char *P;
char *p = map[123],
*p1 = P(); // map uses the same construct inside, causes zero-initialization
assert(!p && !p1); // both will be 0
유형 이름 뒤의 괄호가 new와 차이가 있습니까?를 참조하십시오 . 문제에 대한 자세한 내용은.
원하는 기본값으로 할당하는 사용자 지정 할당자를 제공 할 수 있습니다.
template < class Key, class T, class Compare = less<Key>,
class Allocator = allocator<pair<const Key,T> > > class map;
operator[]
T()
할당자가 무엇을하든 을 호출하여 생성 된 객체를 반환합니다 .
construct
메서드를 호출하지 않습니까? 그것을 바꿀 수 있다고 생각합니다. 그래도 제대로 구성되지 않은 construct
것 이외의 다른 기능을 수행하는 것 같습니다 new(p) T(t);
. 편집 : 어리석은 생각에, 그렇지 않으면 모든 값이 동일 할 것입니다 : P 내 커피는 어디에 ...
operator[]
반환 (*((insert(make_pair(x, T()))).first)).second
. 그래서 내가 뭔가를 놓치고 있지 않는 한이 대답은 틀렸다.
insert
로모그래퍼 T()
하지만 새로운에 대한 할당 get 및 메모리를 사용할 때 내부에 삽입은 T
다음 호출 construct
이다가 주어진 매개 변수와 그 메모리에 T()
. 따라서 실제로 operator[]
다른 것을 반환 하도록 동작을 변경할 수 있지만 할당자는 호출되는 이유를 구별 할 수 없습니다. 그래서 우리 construct
가 매개 변수를 무시하고 우리의 특별한 값을 사용 한다고하더라도 , 그것은 모든 생성 된 요소가 그 값을 가지고 있다는 것을 의미 합니다 .
https://stackoverflow.com/a/2333816/272642 답변을 확장 하면이 템플릿 함수는 std::map
의 key_type
및 mapped_type
typedef를 사용 하여 key
및 유형을 추론합니다 def
. 이러한 typedef가없는 컨테이너에서는 작동하지 않습니다.
template <typename C>
typename C::mapped_type getWithDefault(const C& m, const typename C::key_type& key, const typename C::mapped_type& def) {
typename C::const_iterator it = m.find(key);
if (it == m.end())
return def;
return it->second;
}
이것은 당신이 사용할 수 있습니다
std::map<std::string, int*> m;
int* v = getWithDefault(m, "a", NULL);
같은 인수를 캐스팅 할 필요없이 std::string("a"), (int*) NULL
.
사용하다 std::map::insert()
.
내가이 파티에 꽤 늦었 음을 깨달았지만 operator[]
사용자 정의 기본값 을 사용 하는 동작에 관심이 있다면 (즉, 주어진 키가있는 요소를 찾으십시오. 존재하지 않는 경우 맵에 요소를 삽입 하십시오. 기본값을 선택하고 새로 삽입 된 값 또는 기존 값에 대한 참조를 반환합니다.) C ++ 17 이전에 사용할 수있는 함수가 이미 있습니다 std::map::insert()
.insert
키가 이미 존재하는 경우 실제로 삽입되지 않고 대신 기존 값에 반복자를 반환합니다.
문자열을 정수로 매핑하고 키가 아직없는 경우 기본값 42를 삽입하고 싶다고 가정 해 보겠습니다.
std::map<std::string, int> answers;
int count_answers( const std::string &question)
{
auto &value = answers.insert( {question, 42}).first->second;
return value++;
}
int main() {
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
std::cout << count_answers( "Life, the universe and everything") << '\n';
return 0;
}
42, 43 및 44를 출력해야합니다.
맵 값을 구성하는 비용이 높으면 (키 복사 / 이동 또는 값 유형이 비용이 많이 드는 경우) 상당한 성능 저하가 발생하며 C ++ 17에서는 피할 수 있다고 생각합니다. try_emplace
.