从C ++ STL集合中获取通用key_type

时间:2012-06-29 11:35:17

标签: c++ templates stl map set

我提出了以下代码,该代码演示了一种在STL集合上进行一般迭代并获取密钥值的技术,无论密钥是如何存储的。

这个的上下文是我重构两个函数,它们在两个集合上运行相同的功能:一个是set<int>,另一个是map<int, int>所以在第一种情况下我想要在*it上行动,在it->first上行动(其中it是const_iterator。)

重要的是,我想这样做,因为集合非常大,我不想只是从set创建一个map,所以我只能处理一个特定的类型

#include <map>
#include <set>
#include <iostream>

using namespace std;

// General case for obtaining from, say, a set.
template< typename T >
const typename T::key_type getKey( const typename T::const_iterator& it )
{
    return *it;
}

// Specific case for a map<int,int>
template<>
const map<int, int>::key_type getKey< map<int, int> >( const map<int, int>::const_iterator& it )
{
    return it->first;
}

template< typename T >
void dumpOut( T& coll )
{
    for ( typename T::const_iterator it = coll.begin(); it != coll.end(); ++it )
    {
        const typename T::key_type& a = getKey<T>(it);
        cout << a << endl;
    }
}

int main()
{
    set<int> s1;
    s1.insert(10);
    s1.insert(15);
    s1.insert(20);

    dumpOut< set<int> >( s1 );

    map<int, int> m1;
    m1.insert( pair<int, int>(11, -1) );
    m1.insert( pair<int, int>(16, -1) );
    m1.insert( pair<int, int>(21, -1) );

    dumpOut< map<int, int> >( m1 );

    return 0;
}

我的问题是:是否有可能使map<int,int>的专业案例更加通用,因为无论关键和值实际是什么,这种方法通常都会明确地适用于map。< / p>

任何指针(没有双关语)都会有用。请注意,我不能使用C ++ 11解决方案,尽管我对从学术角度使用它的解决方案感兴趣。感谢。

2 个答案:

答案 0 :(得分:2)

这里有一个C ++语言问题 - 不允许对函数进行部分特化。 所以它不能这么简单:

// Specific case for a map<int,***>
template<typename Value_, typename Comp_, typename Alloc_>
const typename map<int, Value_, Comp_, Alloc_>::key_type getKey< map<int, Value_, Comp_, Alloc_> >( const typename map<int, Value_, Comp_, Alloc_>::const_iterator& it )
{
    return it->first;
}

幸运的是,允许对类进行部分特化 - 所以改为这样:

// work - for let say - sets
template <class Type_>
struct Key { 
   Key(typename Type_::const_iterator it) : value(*it) {}
   typename Type_::key_type value;
};

// work - for map<***>
template <class Key_, class Value_, class Comp_, class Alloc_>
struct Key<map<Key_, Value_,Comp_,Alloc_> > { 
   typedef map<Key_, Value_,Comp_,Alloc_> Type_;
   Key(typename Type_::const_iterator it) : value(it->first) {}
   typename Type_::key_type value;
};


template< typename T >
const typename T::key_type getKey( const typename T::const_iterator& it )
{
    return Key<T>(it).value;
}

我在那里复制了您更改的示例:http://ideone.com/tE2aC

答案 1 :(得分:1)

您需要定义可以在外部定义的特征。您还希望它尝试提取key_type,如果存在,则adn属于defautl情况(如果不存在)(默认情况可以在以后专用)。这需要快速捕捉SFINAE和模板魔法:

//// This is a SFINAE context enabler. If T is defined R is returned
template<class T, class R=void> struct enable_if_type
{
   typedef R type;
};

//// Default case is undefined as you want to get an error if you try to get a key_type from something that has none
template<class T, class Enable=void> struct key_type_of;


//// If T::key_type is a valid expression, extract it
template<class T>
struct key_type_of< T
                  , typename enable_if_type< typename T::key_type>::type
                  >
{
  typedef typename T::key_type type;
};

现在任何带有key_type typedef的类型都会使用它,其他不会编译但你可以使用 key_type_of的专业化,为其提供成功的匹配。

示例:

http://ideone.com/3PxJm

完成此操作后,您实际上可以使用它来获取key_type。要以独立于容器的方式提取密钥本身,您可以创建一个外部key_of函数,您可以为广泛的集合类型进行推广。