1

ID現在、オブジェクトのアドレスを識別子として使用しているクラス テンプレートがあります。

このクラス テンプレートを特殊化して、 が定義されている場合Tは、オブジェクトkey_typeを呼び出しget_key()て、代わりにそれを識別子として使用したいと考えています。もちろん、その値も保存するので、元の実装との重複度が高くなります。

コードの再利用を最大化しながら、これを達成するための最良の方法は何ですか?

以下のコードでID<Bar>std::stringBar const*.

が定義されているかどうかを切り替える方法として、SFINAE を調べてみましたT::key_typeが、関数テンプレートの例しか見つかりませんでした。

コード

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

以下のセス・カーネギーの回答に基づいて、ソリューションをコード化することができました。

解決

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

1 に答える 1

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;

...
于 2013-02-06T06:15:54.777 に答える