如何根据T :: key_type的定义来专门化类模板?

时间:2013-02-06 06:01:15

标签: c++ templates c++11

我有一个类模板ID,它目前使用对象的地址作为标识符。

我希望对此类模板进行专门化,以便T定义key_type时,我会在对象上调用get_key()并使用它,而不是标识符。当然,我也会存储该值,因此与原始实现存在高度重叠。

实现这一目标的最佳方法是什么,同时最大化代码重用?

在下面的代码中,我希望ID<Bar>在其内容中自动使用std::string,而不是Bar const*

我尝试研究SFINAE,作为一种切换是否定义T::key_type的方法,但我只能找到功能模板的示例。

CODE

#include <string>
#include <set>
#include <cassert>

template<typename T>
class ID
{
private:
    T const* m_id;
public:
    ID( T const& t ) :
        m_id( &t )
    { }
    ID( ID const& rhs ) :
        m_id( rhs.m_id )
    { }
    ~ID() { }
    ID& operator=( ID const& rhs )
    {
        if ( &rhs!=this )
            m_id = rhs.m_id;
        return *this;
    }
public:
    bool operator==( ID const& rhs ) const { return m_id==rhs.m_id; }
    bool operator!=( ID const& rhs ) const { return !(*this==rhs);  }
    bool operator<( ID const& rhs ) const  { return m_id<rhs.m_id;  }
    bool operator<=( ID const& rhs ) const { return m_id<=rhs.m_id; }
    bool operator>( ID const& rhs ) const  { return m_id>rhs.m_id;  }
    bool operator>=( ID const& rhs ) const { return m_id>=rhs.m_id; }
};

// -----------------------------------------------------------------------------

struct Foo { };
struct Bar { 
    using key_type = std::string;

    std::string  m_key;
    Bar( std::string const& key ) : m_key( key ) { }
};

int main( int argc, char* argv[] )
{
    Foo f,g;

    ID<Foo> id_f( f ), id_g( g );
    assert( id_f!=id_g );

    std::set<ID<Foo>>  s;
    s.insert( f );
    s.insert( g );

    assert( s.size()==2u );

    Bar h( "abc" ), i( "abc" );
    std::set<ID<Bar>>  s2;
    s2.insert( h );
    s2.insert( i );
    assert( s2.size()==1u );
}

我能够根据Seth Carnegie's answer below编写解决方案。

#include <string>
#include <set>
#include <cassert>

template<typename T>
struct void_ {
    using type = void;
};

// -----------------------------------------------------------------------------

template<typename T, typename = void>
struct ptr_or_key_type {
    using type = T const*;               // our default key_type : ptr
    static type get_key( T const& t ) { return &t; }
};

template<typename T>
struct ptr_or_key_type<T, typename void_<typename T::key_type>::type> {
    using type = typename T::key_type;   // the specialised key_type
    static type get_key( T const& t ) { return t.get_key(); }
};

// -----------------------------------------------------------------------------

template<typename T>
class ID
{
private:
  typename ptr_or_key_type<T>::type m_id;

public:
    ID( T const& t ) :
        m_id( ptr_or_key_type<T>::get_key( t ))
    { }
    ID( ID const& rhs ) :
        m_id( rhs.m_id )
    { }
    ~ID() { }
    ID& operator=( ID const& rhs )
    {
        if ( &rhs!=this )
            m_id = rhs.m_id;
        return *this;
    }
public:
    bool operator==( ID const& rhs ) const { return m_id==rhs.m_id; }
    bool operator!=( ID const& rhs ) const { return !(*this==rhs);  }
    bool operator<( ID const& rhs ) const  { return m_id<rhs.m_id;  }
    bool operator<=( ID const& rhs ) const { return m_id<=rhs.m_id; }
    bool operator>( ID const& rhs ) const  { return m_id>rhs.m_id;  }
    bool operator>=( ID const& rhs ) const { return m_id>=rhs.m_id; }
};

// -----------------------------------------------------------------------------

struct Foo { };
struct Bar { 
    using key_type = std::string;

    std::string  m_key;
    Bar( std::string const& key ) : m_key( key ) { }
    std::string const& get_key() const { return m_key; }
};

int main( int argc, char* argv[] )
{
    Foo f,g;

    ID<Foo> id_f( f ), id_g( g );
    assert( id_f!=id_g );

    std::set<ID<Foo>>  s;
    s.insert( f );
    s.insert( g );

    assert( s.size()==2u );

    Bar h( "abc" ), i( "abc" );
    std::set<ID<Bar>>  s2;
    s2.insert( h );
    s2.insert( i );
    assert( s2.size()==1u );
}

1 个答案:

答案 0 :(得分:4)

您可以使用SFINAE一小组特征类:

template<typename T>
struct void_ {
    using type = void;
};

template<typename T, typename = void>
struct key_type_or_id {
     using type = T const*; // Our default key_type
};

template<typename T>
struct key_type_or_id<T, typename void_<typename T::key_type>::type> {
    using type = typename T::key_type; // The specialised key_type
};

...

template<typename T>
class ID
{
private:
    typename key_type_or_id<T>::type m_id;

...