【问题标题】:Obtaining generic key_type from C++ STL collections从 C++ STL 集合中获取泛型 key_type
【发布时间】:2012-06-29 11:35:17
【问题描述】:

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

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

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

#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&lt;int,int&gt; 的专用案例更通用一点,因为无论键和值实际上是什么,这种方法显然都适用于 map。

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

【问题讨论】:

  • 并非所有的集合都有 key_types,所以我不明白一般情况下如何做到这一点。
  • @juanchopanza:对不起,你是对的。我真正的意思是associative 集合。 generic,我的真正意思是“对于我的情况来说足够通用”:)

标签: c++ templates stl map set


【解决方案1】:

这里有一个 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

【讨论】:

    【解决方案2】:

    您需要定义一个可以在外部定义的 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 函数,您可以将其泛化为广泛的集合类型。

    【讨论】:

    • 谢谢,这很聪明,但我不确定它对我自己的情况是否有用。我已经可以在我感兴趣的两个实例中获得 key_type,并且我有兴趣采用一种方法将 map&lt;int,int&gt; 的特殊情况替换为map&lt;K,V&gt; 的更一般情况
    猜你喜欢
    • 2020-03-07
    • 2016-08-24
    • 2015-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多