我有一个对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
的结果而无需定义隐式转换运算符。)
join_range_keys
的定义非常相似,使用first_type
和p.first
代替second_type
和p.second
。我假设类似的定义适用于加入std::set
和std::multiset
键,但我没有任何需要。
我可以将这些函数应用于具有各种类型字符串的容器。对于键和值类型,map
和multimap
与string
和wstring
的任意组合的任意组合似乎都有效:
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"));
这给我留下了几个问题:
join_values
自动推断出模板参数类型,这样我每次都不需要用join_values<MapType>
来调用它?join_values
和join_keys
函数和仿函数以避免重复大部分代码?我确实找到了一个基于std::accumulate
的稍微简单的解决方案,但它似乎需要对该范围内的每个元素进行整个字符串的两次完整复制操作,因此,就我所知,它的效率要低得多
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()));
答案 1 :(得分:2)
对于任何有兴趣的人,我都会根据keraba的意见找到以下解决方案。
我确实需要做一些改变,特别是:
T
模板参数成为依赖类型名称,以便编译器自动推断它(允许引用的文字自动转换为字符串对象)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的设计更合适。