14

の場合std::map<std::string, std::string> variables、私はこれをしたいと思います:

BOOST_CHECK_EQUAL(variables["a"], "b");

唯一の問題は、このコンテキストでvariablesconst、動作しoperator[]ないことです:(

現在、これにはいくつかの回避策があります。をキャストしたり、それをラップする関数constを使用しvariables.count("a") ? variables.find("a")->second : std::string()たり、作成したりします。これらのどれも私にはoperator[]. 私は何をすべきか?これを(美しく)行う標準的な方法はありますか?

編集:誰も与えたくない答えを述べるだけです:いいえ、C ++でこれを行う便利で美しい標準的な方法はありません。サポート機能を実装する必要があります。

4

7 に答える 7

11
template <typename K, typename V>
V get(std::map<K, V> const& map, K const& key)
{
    std::map<K, V>::const_iterator iter(map.find(key));
    return iter != map.end() ? iter->second : V();
}

コメントに基づく改善された実装:

template <typename T>
typename T::mapped_type get(T const& map, typename T::key_type const& key)
{
    typename T::const_iterator iter(map.find(key));
    return iter != map.end() ? iter->second : typename T::mapped_type();
}
于 2008-09-30T11:38:08.240 に答える
11

operator[] on map<> は、デフォルトで構築された文字列が存在しない場合にエントリを作成するため、const のキャストは間違っています。マップが実際に不変ストレージにある場合、マップは失敗します。operator[] は代入を許可するために非 const 参照を返すため、これはそうであるに違いありません。(例: m[1] = 2)

比較を実装する簡単な無料関数:

template<typename CONT>
bool check_equal(const CONT& m, const typename CONT::key_type& k,
                    const typename CONT::mapped_type& v)
{
    CONT::const_iterator i(m.find(k));
    if (i == m.end()) return false;
    return i->second == v;
}

シンタックス シュガーについて考え、何か思いついたら更新します。

...

即時のシンタックス シュガーには、map<>::find() を実行し、map<>::const_iterator をラップする特別なクラスを返す無料の関数が含まれており、operator==() と operator!=() をオーバーロードして許可しています。マッピングされたタイプとの比較。したがって、次のようなことができます:

if (nonmutating_get(m, "key") == "value") { ... }

私はそれが以下よりもはるかに優れているとは確信していません:

if (check_equal(m, "key", "value")) { ... }

そして、それは確かにはるかに複雑であり、何が起こっているのかはあまり明白ではありません.

イテレータをラップするオブジェクトの目的は、デフォルトで構築されたデータ オブジェクトを持たないようにすることです。気にしない場合は、「get」回答を使用してください。

将来の用途を見つけることを期待して、比較よりも取得が優先されるというコメントに応えて、次のコメントがあります。

  • つまり、「check_equal」という関数を呼び出すと、オブジェクトを作成せずに等値比較を行っていることが明確になります。

  • 必要になったときにだけ機能を実装することをお勧めします。それ以前に何かをするのは、しばしば間違いです。

  • 状況によっては、既定のコンストラクターに副作用がある場合があります。比較しているのなら、なぜ余分なことをするのですか?

  • SQL 引数: NULL は空の文字列と同等ではありません。コンテナにキーが存在しないことは、デフォルトで構築された値でコンテナに存在するキーと本当に同じですか?

そうは言っても、デフォルトで構築されたオブジェクトは、非 const コンテナーで map<>::operator[] を使用することと同等です。また、デフォルトで構築されたオブジェクトを返す get 関数が現在必要とされている場合もあります。私は過去にその要件を持っていたことを知っています。

于 2008-09-30T11:45:23.900 に答える
5

興味深いことに、受け入れられた get 実装でテンプレート型の検出を行う方法は 2 つあります (値を取得するか、デフォルトで構築されたオブジェクトを返す方法)。1つは、受け入れられたことを実行して、次のことを行うことができます。

template <typename K, typename V>
V get1(const std::map<K, V>& theMap, const K const key)
{
    std::map<K, V>::const_iterator iter(theMap.find(key));
    return iter != theMap.end() ? iter->second : V();
}

または、マップ タイプを使用して、それからタイプを取得できます。

template<typename T>
typename T::mapped_type
get2(const T& theMap, const typename T::key_type& key)
{
    typename T::const_iterator itr = theMap.find(key);
    return itr != theMap.end() ? itr->second : typename T::mapped_type();
}

これの利点は、渡されるキーのタイプがタイプの検出に関与せず、暗黙的にキーに変換できるものになる可能性があることです。例えば:

std::map<std::string, int> data;
get1(data, "hey"); // doesn't compile because the key type is ambiguous
get2(data, "hey"); // just fine, a const char* can be converted to a string
于 2008-09-30T14:03:58.733 に答える
5

find慣用的な形です。キャストアウェイconstは、ほとんどの場合、悪い考えです。書き込み操作が実行されないことを保証する必要があります。これは、マップ上の読み取りアクセスに合理的に期待できますが、仕様ではこれについて何も述べていません。

値が存在することがわかっている場合は、もちろん、を使用したテストを差し控えることができますcount(これは、マップを 2 回トラバースすることを意味するため、とにかく非常に非効率的です。要素が存在するかどうかわからなくても、私はこれを使用しません。代わりに次のようにします。

T const& item(map<TKey, T> const& m, TKey const& key, T const& def = T()) {
    map<TKey, T>::const_iterator i = m.find(key);
    return i == m.end() ? def : i->second;
}

/EDIT:クリスが正しく指摘したように、タイプのオブジェクトのデフォルト構築は、特にこのオブジェクトが実際には必要ない場合でも行われるため(エントリが存在するため)、コストがかかるT 可能性があります。defこの場合、上記のケースの引数にデフォルト値を使用しないでください。

于 2008-09-30T11:36:10.460 に答える
1

実際、operator[] は std::map では非 const です。マップにキーと値のペアが存在しない場合は自動的に挿入されるからです。(ああ、副作用!)

正しい方法はmap::find、返されたイテレータが有効な場合 ( != map.end())を使用してsecond、示したように を返すことです。

map<int, int> m;
m[1]=5; m[2]=6; // fill in some couples
...
map<int,int>::const_iterator it = m.find( 3 );
if( it != m.end() ) {
    int value = it->second;
    // ... do stuff with value
}

使用している std::map のサブクラスに a を追加し、見つかったキーをアサートしてから、 を返すことできます。map::operator[]( const key_type& key ) constit->second

于 2008-09-30T11:36:45.263 に答える
0
std::map<std::string, std::string>::const_iterator it( m.find("a") );
BOOST_CHECK_EQUAL( 
                     ( it == m.end() ? std::string("") : it->second ), 
                     "b" 
                 );

それは私にはそれほど悪くはありません...私はおそらくこれのための関数を書きません。

于 2008-09-30T16:40:00.650 に答える
0

マップ コンテナーを特殊化するという xtofl のアイデアをフォローアップします。以下はうまくいきますか?

template <typename K,typename V>  
struct Dictionary:public std::map<K,V>  
{  
  const V& operator[] (const K& key) const  
  {  
    std::map<K,V>::const_iterator iter(this->find(key));  
    BOOST_VERIFY(iter!=this->end());  
    return iter->second;  
  }  
};  
于 2009-06-16T07:51:41.670 に答える