2009-07-21 3 views
3

STL 문자열에서 작동하는 조인 함수가 있습니다. 나는이 같은 컨테이너에 적용 할 수 있도록하려면 :C++ STL 연관 컨테이너의 키/값 결합

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

즉, 컬렉션에 일치하는 모든 키를 찾아 주어진 분리 된 단일 문자열로 값을 연결.

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

(나는 것을 깨닫게 : lower_bound()와 키의 범위 upper_bound(), 컨테이너의 전체 내용에 대한 begin()/ end() 등과 같은 일이 ..

내가 얻을 수있는 가장 가까운는 다음과 std::string에서 상속하거나 키/값 유형이 무엇이든 일반적으로 나쁜 생각으로 간주되지만 오버로드 나 오버라이드 기능이 아니며 가상 소멸자가 필요하지 않습니다. 암시 적 변환 연산자를 정의 할 필요없이 for_each의 결과를 사용하십시오.

second_typep.second 대신 first_typep.first을 사용하는 매우 유사한 정의가 join_range_keys입니다. 나는 비슷한 정의가 std::setstd::multiset 키를 합치기 위해 작동한다고 가정하고 있지만, 그것에 대한 필요성은 전혀 없습니다.

다양한 유형의 문자열이있는 컨테이너에이 기능을 적용 할 수 있습니다.

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. 내가 몇 가지 쉬운 방법 실종 키와 값 유형 stringwstring의 조합으로 mapmultimap의 조합이 작동하는 것 같다 똑같은 일을 완수합니까? 함수 서명은 특히 지나치게 복잡해 보입니다.
  2. join_values은 매번 join_values<MapType>을 호출 할 필요가 없도록 템플릿 매개 변수 유형을 자동으로 추론 할 수 있습니까?
  3. join_valuesjoin_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())); 
2

을 참고하시기 바랍니다 관심있는 사람들을 위해, 나는 keraba의 입력에 따라 다음과 같은 솔루션에왔다.

좀 구체적으로 변화 할 필요가 않았다 컴파일러가 자동으로 (인용 리터럴이 자동으로 변환 할 수 있도록 그것을 추론 할 수 있도록 단락 캐릭터에 대한 의존의 형태 이름을 T 템플릿 매개 변수를 만들기

  1. 을 문자열 개체)
  2. string에서 파생 된 functor를 사용하여 반복기에 선언 된 종속 이름이 const이라는 사실을 알아 냈으므로 join()에 정의 된 로컬 임시는 const으로 끝나며 따라서 수정할 수 없습니다. 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()); 
    

    뿐만 아니라 : 위해


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

이 있습니다.

이것은 여전히 ​​복잡하지만 원래 게시물에서 항목 2와 3을 해결하고 STL 디자인에 훨씬 더 잘 맞는 것처럼 보입니다.

관련 문제