2009-07-21 9 views
3

私はSTL文字列で動作する結合関数を持っています。私はこのようなコンテナにそれを適用することができるようにしたい:C++ STL連想コンテナからのキー/値の結合

getFoos(const std::multimap<std::string, std::string>& map) { 
    return join_values(",", map.equal_range("foo")); 

つまり、コレクション内のすべての一致するキーを見つけて、与えられたセパレータを単一の文字列に値を連結します。コンテナの内容全体のためのキーの範囲についてlower_bound()upper_bound()begin()/end()、などと同じこと..

私が得ることができる最も近いは以下の通りです:

template <typename T> 
struct join_range_values : public T::const_iterator::value_type::second_type { 
    typedef typename T::const_iterator::value_type pair_type; 
    typedef typename pair_type::second_type value_type; 

    join_range_values(const value_type& sep) : sep(sep) { } 

    void operator()(const pair_type& p) { 
     // this function is actually more complex... 
     *this += sep; 
     *this += p.second; 
    } 
private: 
    const value_type sep; 
}; 

template <typename T> 
typename T::const_iterator::value_type::second_type join_values(
    const typename T::const_iterator::value_type::second_type& sep, 
    const std::pair<typename T::const_iterator, typename T::const_iterator>& range) { 
    return std::for_each(range.first, range.second, join_range_values<T>(sep)); 
} 

(私は実感してstd::stringから継承するか、キー/値の型が何であるかは一般的に悪い考えですが、関数のオーバーロードやオーバーライドではないため、仮想デストラクタは必要ありません。暗黙の変換演算子を定義することなく、for_eachの結果を使用してください)。

second_typep.secondの代わりにfirst_typep.firstを使用して、非常に似た定義があります。join_range_keysです。私は、同様の定義がstd::setstd::multisetの鍵を結合するために働くと仮定していますが、私はそれを必要としていません。

これらの関数は、さまざまな種類の文字列を持つコンテナに適用できます。キーと値の種類のstringwstringの任意の組み合わせでmapmultimapの任意の組み合わせが動作しているようです:

typedef std::multimap<std::string, std::string> NNMap; 
const NNMap col; 
const std::string a = join_keys<NNMap>(",", col.equal_range("foo")); 
const std::string b = join_values<NNMap>(",", col.equal_range("foo")); 

typedef std::multimap<std::string, std::wstring> NWMap; 
const NWMap wcol; 
const std::string c = join_keys<NWMap>(",", wcol.equal_range("foo")); 
const std::wstring d = join_values<NWMap>(L",", wcol.equal_range("foo")); 

typedef std::multimap<std::wstring, std::wstring> WWMap; 
const WWMap wwcol; 
const std::wstring e = join_keys<WWMap>(L",", wwcol.equal_range(L"foo")); 
const std::wstring f = join_values<WWMap>(L",", wwcol.equal_range(L"foo")); 

これは、いくつかの質問を私に残します:

  1. 私はにいくつかの簡単な方法をしないのです同じことを達成する?関数シグネチャは特に複雑すぎるようです。
  2. join_valuesは、毎回join_values<MapType>と呼ぶ必要がないようにテンプレートパラメータタイプを自動的に推測させる方法はありますか?
  3. join_valuesjoin_keysの関数と関数をリファクタリングしてコードの大部分を複製しないようにするにはどうすればよいですか?

私はstd::accumulateに基づいて、わずかに簡単な解決策を見つけたが、範囲内の各要素の文字列全体の2つの完全なコピー操作を必要としているようですので、それは私の知る限り、あまり効率的です。

template <typename T> 
struct join_value_range_accum : public T::const_iterator::value_type::second_type 
{ 
    typedef typename T::const_iterator::value_type::second_type value_type; 
    join_value_range_accum(const value_type& sep) : sep(sep) {} 

    using value_type::operator=; 
    value_type operator+(const typename T::const_iterator::value_type& p) 
    { 
     return *this + sep + p.second; 
    } 
private: 
    const value_type sep; 
}; 

typedef std::multimap<std::string, std::string> Map; 
Map::_Pairii range = map.equal_range("foo"); 
std::accumulate(range.first, range.second, join_value_range_accum<Map>(",")); 
+0

オハイオ州の男の子、私はこれを最初に消化する必要があります...ホールド! –

答えて

6

STLアルゴリズムは通常、コンテナではなくイテレータで動作するため、次のようなものをお勧めします。

template <typename T, typename Iterator> 
T join(
    const T sep, 
    Iterator b, 
    Iterator e) 
{ 
    T t; 

    while (b != e) 
     t = t + *b++ + sep; 

    return t; 
} 

次に、キーまたは値を引き出すイテレータが必要です。ここでは例です:

使用する方法
template <typename Key, typename Iterator> 
struct KeyIterator 
{ 
    KeyIterator(
     Iterator i) 
     :_i(i) 
    { 
    } 

    KeyIterator operator++() 
    { 
     ++_i; 
     return *this; 
    } 

    bool operator==(
     KeyIterator ki) 
    { 
     return _i = ki._i; 
    } 

    typename Iterator::value_type operator*() 
    { 
     return _i->first; 
    } 
}; 

string s = join(",", KeyIterator(my_map.begin()), KeyIterator(my_map.end())); 
2

をちょうどFYI興味がある人のために、私はkerabaの入力に基づいて、以下のソリューションに来ました。

私は具体的には、いくつかの変更を加えなければならなかった:

  1. を区切り文字列に依存するタイプ名のTテンプレートパラメータを作るコンパイラが引用されたリテラルは自動的に変換することができる(それを自動的に推測なるように文字列オブジェクト)
  2. 文字列から派生したファンクタを使用して、イテレータで宣言された従属名がconstであるため、join()で定義されているローカル仮定値がconstになり、変更不可能になります。

template <typename I> 
struct MapKeyIterator : public I 
{ 
    typedef typename I::value_type::first_type value_type; 
    MapKeyIterator(I const &i) : I(i) { } 
    value_type const & operator*() const { return (*this)->first; } 
}; 

template <typename I> 
struct MapValueIterator : public I 
{ 
    typedef typename I::value_type::second_type value_type; 
    MapValueIterator(I const &i) : I(i) { } 
    value_type const & operator*() const { return (*this)->second; } 
}; 

template <typename I> 
struct join_functor : public I::value_type 
{ 
    typedef typename I::value_type value_type; 
    join_functor(value_type const &sep) : sep(sep) { } 
    void operator()(value_type const &s) 
    { 
     *this += s; 
     *this += sep; 
    } 
private: 
    const value_type sep; 
}; 

template <typename I> 
typename I::value_type join(typename I::value_type const &sep, I beg, I const &end) 
{ 
    return std::for_each(beg, end, join_functor<I>(sep)); 
} 

template <typename I> 
typename I::value_type::first_type join_keys(typename I::value_type::first_type const &sep, I const &beg, I const &end) 
{ 
    return join(sep, MapKeyIterator<I>(beg), MapKeyIterator<I>(end)); 
} 
template <typename I> 
typename I::value_type::first_type join_keys(typename I::value_type::first_type const &sep, std::pair<I, I> const &ip) 
{ 
    return join(sep, MapKeyIterator<I>(ip.first), MapKeyIterator<I>(ip.second)); 
} 
template <typename I> 
typename I::value_type::second_type join_values(typename I::value_type::second_type const &sep, I const &beg, I const &end) 
{ 
    return join(sep, MapValueIterator<I>(beg), MapValueIterator<I>(end)); 
} 
template <typename I> 
typename I::value_type::second_type join_values(typename I::value_type::second_type const &sep, std::pair<I, I> const &ip) 
{ 
    return join(sep, MapValueIterator<I>(ip.first), MapValueIterator<I>(ip.second)); 
} 

これはを可能にする:std::string又はstd::wstringのいずれかに基づいて、コンテナと

join(",", set.lower_bound("f"), set.upper_bound("g")); 

join_keys(",", map.equal_range("foo")); 
join_values(",", map.equal_range("foo")); 
join_values(",", map.begin(), map.end()); 

ならびに。

これはまだかなり複雑ですが、オリジナルの投稿からアイテム2と3が解決され、STLのデザインがはるかによく適合しているようです。

関連する問題