C++ 如何根据T::key_类型的定义专门化类模板?

C++ 如何根据T::key_类型的定义专门化类模板?,c++,templates,c++11,C++,Templates,C++11,我有一个类模板ID,它当前使用对象的地址作为其标识符 我希望对该类模板进行专门化,以便如果T定义了key\u类型,我将调用对象上的get\u key(),并将其用作标识符。当然,我还将存储该值,因此将与原始实现有高度重叠 在最大化代码重用的同时,实现这一点的最佳方法是什么 在下面的代码中,我希望ID自动使用std::string而不是Bar const* 我试着研究SFINAE,作为一种切换是否定义了T::key\u type的方法,但我只能找到函数模板的示例 代码 #include <s

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

我希望对该类模板进行专门化,以便如果
T
定义了
key\u类型
,我将调用对象上的
get\u key()
,并将其用作标识符。当然,我还将存储该值,因此将与原始实现有高度重叠

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

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

我试着研究SFINAE,作为一种切换是否定义了
T::key\u 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 );
}
#包括
#包括
#包括
模板
类ID
{
私人:
T const*m_id;
公众:
ID(T常数和T):
m_id&t)
{ }
ID(ID常量和rhs):
m_id(rhs.m_id)
{ }
~ID(){}
ID和运算符=(ID常量和rhs)
{
如果(&rhs!=此)
m_id=rhs.m_id;
归还*这个;
}
公众:
布尔运算符==(ID const&rhs)const{return m_ID==rhs.m_ID;}
布尔运算符!=(ID const&rhs)const{return!(*this==rhs);}
布尔运算符=rhs.m_id;}
};
// -----------------------------------------------------------------------------
结构Foo{};
结构条{
使用key_type=std::string;
std::字符串m_键;
Bar(std::string const&key):m_key(key){}
};
int main(int argc,char*argv[])
{
傅f,g,;
ID_f(f),ID_g(g);
断言(id\u f!=id\u g);
std::集s;
s、 插入(f);
s、 插入(g);
断言(s.size()=2u);
酒吧h(“abc”)、i(“abc”);
std::set s2;
s2.插入(h);
s2.插入(i);
断言(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 );
}
#包括
#包括
#包括
模板
结构无效{
使用类型=无效;
};
// -----------------------------------------------------------------------------
模板
结构ptr\u或\u键\u类型{
using type=T const*;//我们的默认键类型:ptr
静态类型get_key(T const&T){return&T;}
};
模板
结构ptr\u或\u键\u类型{
using type=typename T::key\u type;//专门的key\u类型
静态类型get_key(T const&T){返回T.get_key();}
};
// -----------------------------------------------------------------------------
模板
类ID
{
私人:
typename ptr_或_key_type::type m_id;
公众:
ID(T常数和T):
m_id(ptr_或_key_type::get_key(t))
{ }
ID(ID常量和rhs):
m_id(rhs.m_id)
{ }
~ID(){}
ID和运算符=(ID常量和rhs)
{
如果(&rhs!=此)
m_id=rhs.m_id;
归还*这个;
}
公众:
布尔运算符==(ID const&rhs)const{return m_ID==rhs.m_ID;}
布尔运算符!=(ID const&rhs)const{return!(*this==rhs);}
布尔运算符=rhs.m_id;}
};
// -----------------------------------------------------------------------------
结构Foo{};
结构条{
使用key_type=std::string;
std::字符串m_键;
Bar(std::string const&key):m_key(key){}
std::string const&get_key()const{return m_key;}
};
int main(int argc,char*argv[])
{
傅f,g,;
ID_f(f),ID_g(g);
断言(id\u f!=id\u g);
std::集s;
s、 插入(f);
s、 插入(g);
断言(s.size()=2u);
酒吧h(“abc”)、i(“abc”);
std::set s2;
s2.插入(h);
s2.插入(i);
断言(s2.size()=1u);
}

您可以使用SFINAE作为traits类的一个小组合:

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;

...
模板
结构无效{
使用类型=无效;
};
模板
结构键类型或id{
using type=T const*;//我们的默认键类型
};
模板
结构键类型或id{
using type=typename T::key\u type;//专门的key\u类型
};
...
模板
类ID
{
私人:
typename key\u type\u或\u id::type m\u id;
...

您可以使用SFINAE作为traits类的一个小组合:

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;

...
模板
结构无效{
使用类型=无效;
};
模板
结构键类型或id{
using type=T const*;//我们的默认键类型
};
模板
结构键类型或id{
using type=typename T::key\u type;//专门的key\u类型
};
...
模板
类ID
{
私人:
typename key\u type\u或\u id::type m\u id;
...

void_uu作为专业化标签的使用令人兴奋:我需要一段时间才能了解这一点one@kfmfe04它工作的原因是,您正在创建一个常规模板,其中第二个模板参数默认为
void
,然后(如果
T
有一个成员
key\u type
)你专门针对一般情况。这很奇怪,但是因为使用了默认的
void
(你专门针对它),编译器选择了更专业的类型,并且没有出现双重定义错误。请参阅一个直截了当的示例。+1 ty是一个很好的示例-现在它更有意义了(我在向后看)有趣的是,
std::cout@kfmfe04好吧,只是
s
其中
T
是任何类型都会让你得到专业化,而
b
没有被定义为静态成员(省略第二种类型也会让你得到专业化,因为
扩展为
)。确认-当然:(编译器将首先匹配专业)!头脑发麻-休息一下。用void_uuu作为专业化的标签令人兴奋:我需要一段时间才能清醒过来one@kfmfe04它工作的原因是,您正在创建一个常规模板,其中第二个模板参数默认为
void
,然后(如果
T
有一个成员
key\u type