ID
現在、オブジェクトのアドレスを識別子として使用しているクラス テンプレートがあります。
このクラス テンプレートを特殊化して、 が定義されている場合T
は、オブジェクトkey_type
を呼び出しget_key()
て、代わりにそれを識別子として使用したいと考えています。もちろん、その値も保存するので、元の実装との重複度が高くなります。
コードの再利用を最大化しながら、これを達成するための最良の方法は何ですか?
以下のコードでID<Bar>
はstd::string
、Bar 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 );
}