2013-07-18 4 views
1

Я проектировал классы, все следуют идиомы, что их общий тип указателя доступен в их имен, используя такой код:Получение типа производного класса во время компиляции в C++

class ClassName 
    { 
    public: 
    typedef std::shared_ptr<ClassName> ptr; 
    } 

делает возможным чтобы написать такой код:

ClassName::ptr p=std::make_shared<ClassName>(); 

Теперь я хотел бы включить этот код в базовый класс. Это позволит мне магически добавить поддержку weak_ptr только в одном месте и укажет, какие классы соответствуют этому протоколу, и, как правило, упрощает создание возможностей.

Я вынужден использовать шаблоны?

Один из способов - следовать подходу enable_shared_from_this <>.

template <typename T> 
class SmartPointer 
    { 
    public: 
    typedef std::shared_ptr<T> ptr; 
    typedef std::weak_ptr<T> wptr; 
    } 

позволяет мне определить классы, как это:

class ClassName:public SmartPointer<ClassName> 
    ... 

но меня поражает, как неуклюжий, чтобы указать ClassName в качестве параметра шаблона. То, что я хотел бы сделать, это быть в состоянии идентифицировать тип производного класса во время компиляции, подставляя в течение derivedClassType() ниже:

class SmartPointer 
    { 
    public: 
    typedef std:shared_ptr<derivedClassType()> ptr; 
    typedef std:weak_ptr<derivedClassType()> wptr; 
    } 

class ClassName: public SmartPointer 
    ... 

кажется, что компилятор должен знать, что информация во время компиляции своего рода подразумеваемый шаблонный параметр ... Это имеет дополнительное преимущество, гарантируя, что тип, на который делается ссылка, является производным классом, а не базовым классом.

Есть ли синтаксис, который сделал бы это возможным?

+5

№. – Rapptz

+1

Почему? 'auto p = std :: make_shared ();' проще и не требует какого-либо из того, что вы делаете. –

+2

И, что интересно, компилятор не знает эту информацию во время компиляции, потому что «SmartPointer» не имеет базового класса. Это _is_ базовый класс. –

ответ

1

Подход, используемый enable_shared_from_this, называется CRTP, «любопытно повторяющимся шаблоном шаблона». Вам нужно указать производный класс; для шаблона базового класса нет способа спросить, кто использует его в качестве базового класса.

Но вы можете наследовать от enable_shared_from_this в базе CRTP и не указывать имя производного класса дважды.

1

Итак, я собираюсь обмануть, изменив ваш синтаксис.

template<typename T> 
using Ptr = T::template ptr<T>; 
template<typename T> 
using WPtr = T::template wptr<T>; 

struct Pointerable { 
    template<typename T> 
    using ptr = std::shared_ptr<T>; 
    template<typename T> 
    using wptr = std::weak_ptr<T>; 
}; 

class Foo : public Pointerable { 

}; 

Ptr<Foo> x = std::make_shared<Foo>(...); 
WPtr<Foo> y = x; 

здесь, единственная цель Pointerable является помечать типы, которые вы хотите использовать. Ptr<Foo> и WPtr<Foo> могли быть написаны для работы на любом классе:

template<typename T> 
using Ptr = std::shared_ptr<T>; 
template<typename T> 
using WPtr = std::weak_ptr<T>; 

, но я полагаю, вы хотите, чтобы иметь возможность пометить объекты как «управляемый смарт-указатели» по какой-либо причине.

Мы могли бы даже сделать Pointerable совершенно пустым, если вам не нравится, что template шаблонного в нем:

struct Pointerable {}; 

и добавить класс качества:

template<typename T> 
struct is_pointerable : std::is_base_of<Pointerable, T> {}; 

, который мы затем использовать:

template<typename T, typename=typename std::enable_if< is_pointerable<T>::value >::type > 
using Ptr = std::shared_ptr<T>; 

, но я думаю, что эта плита работает лучше - SFINAE using похоже overkill.

+0

Псевдонимы шаблонов не могут быть перегружены, и я не думаю, что SFINAE применимо к ним. – Potatoswatter

+0

Спасибо за этот подробный ответ. Я все еще распаковываю его и замечаю нюанс синтаксиса, когда я иду (используя vs typedef, например). Я думаю, что ваш альтернативный синтаксис в порядке, он просто торгует Ptr для класса :: ptr, который, я думаю, в порядке. Я также думаю, что, если я приму встраиваемый Pointerable, я мог бы создать альтернативную версию, обернутую вокруг boost :: intrusive_ptr, где это необходимо, и использовать тот же синтаксис Ptr . – Omegaman

+0

Да, причина, по которой вы используете «Pointerable», должна иметь «SharedPointerable» и «IntrusivePointerable», с фабрикой, которая берет тип и производит интеллектуальный указатель. – Yakk

Смежные вопросы