2

我想出了以下代码,它演示了一种在 STL 集合上进行通用迭代并获取键值的技术,而不管键是如何存储的。

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

重要的是,我想这样做,因为集合非常大,我不想简单地setmapjust 创建一个,所以我只能处理一种特定类型。

#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

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

4

2 回答 2

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

于 2012-06-29T12:44:30.470 回答
1

您需要定义一个可以在外部定义的 Traits。您还希望它尝试提取 key_type(如果存在),如果不存在则属于默认情况(默认情况下可以稍后专门化)。这需要 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 函数,您可以将其泛化为广泛的集合类型。

于 2012-06-29T11:59:27.683 回答