3

STL 文字列を操作する結合関数があります。次のようなコンテナに適用できるようにしたい:

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

つまり、コレクション内の一致するすべてのキーを検索し、指定されたセパレーターを使用して値を 1 つの文字列に連結します。キーの範囲、コンテナの内容全体などについても同じ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暗黙の変換演算子を定義しなくても、 の結果を直接使用できます。)

との代わりにとjoin_range_keysを使用するには、非常によく似た定義があります。結合とキーについても同様の定義が機能すると思いますが、その必要はありませんでした。first_typep.firstsecond_typep.secondstd::setstd::multiset

これらの関数を、さまざまな型の文字列を含むコンテナーに適用できます。キーと値のタイプのとの任意の組み合わせmapmultimapの任意の組み合わせが機能するようです。stringwstring

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_values関数とjoin_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>(","));
4

2 に答える 2

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()));
于 2009-07-21T02:53:22.637 に答える
2

興味のある方の参考までに、ケラバの意見に基づいて次の解決策にたどり着きました。

具体的には、いくつかの変更を加える必要がありました。

  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));
}

これにより、次のことが可能になります。

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

としても:

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

std::stringまたは のいずれかに基づくコンテナーを使用しますstd::wstring

これはまだかなり複雑ですが、最初の投稿の項目 2 と 3 を解決し、STL の設計によりよく適合するようです。

于 2009-07-21T23:06:19.427 に答える