キーが存在しない場合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");
ラッパークラスを使用した同様の実装を次に示します。これは、Pythonget()
の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
、まさにこれを行うものを提供します。値コンストラクターのキーと引数のリストを受け取り、ペアを返します:aniterator
と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(古いデータの計画)型などの整数型)の場合、値はゼロで初期化されます(または値の初期化によってゼロになります(これは事実上です)。同じこと)、使用されているC ++のバージョンによって異なります)。
とにかく、肝心なのは、単純なタイプのマップは新しいアイテムを自動的にゼロ初期化するということです。そのため、場合によっては、デフォルトの初期値を明示的に指定することを心配する必要はありません。
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
'sを使用します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
。