我有一个类模板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 );
}
答案 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;
...