Как объявить статические функции и typedefs публичными, которые не могут быть унаследованы? - PullRequest
0 голосов
/ 26 февраля 2011

Как и многие другие пользователи C ++ 0x, я пытаюсь создать интерфейс интеллектуальных указателей для своих проектов.В идеале я хотел бы, чтобы синтаксис был таким, скрывая оба типа и std::make_shared<T>() вызовы.

Foo::Ptr foo = Foo::shared();
Foo::UPtr unique_foo = Foo::unique();

Я бы хотел разместить объявления внутри структуры для наследования от:

template <class T>
struct SmartDefs
{
   typedef std::shared_ptr<T> Ptr;
   typedef std::unique_ptr<T> UPtr;

   template <class... P>
   static Ptr shared(P&&... p)
   {
      return std::make_shared<T>(std::forward<P>(p)...);
   }

   template <class... P>
   static UPtr unique(P&&... p)
   {
      return std::unique_ptr<T>(new T(std::forward<P>(p)...));
   }
};

Теперь это работает само по себе, но когда в картину вступает наследование, у меня возникают проблемы, так как typedefs и shared / unique статические методы определяются дважды.Мне удалось обойти эту проблему, используя некоторые уродливые макросы, частное наследование, и мне нужно больше печатать, но я бы хотел избежать этого:

#include <memory>
#include <iostream>

template <class T>
struct SmartDefs
{
   typedef std::shared_ptr<T> Ptr;
   typedef std::unique_ptr<T> UPtr;

   template <class... P>
   static Ptr shared(P&&... p)
   {
      return std::make_shared<T>(std::forward<P>(p)...);
   }

   template <class... P>
   static UPtr unique(P&&... p)
   {
      return std::unique_ptr<T>(new T(std::forward<P>(p)...));
   }
};

#define DECL_SMART(type) using SmartDefs< type >::Ptr; \
   using SmartDefs< type >::UPtr; \
   using SmartDefs< type >::shared; \
   using SmartDefs< type >::unique

class Foo : private SmartDefs<Foo>
{
   public:
      DECL_SMART(Foo);
      virtual void foo() const { std::cout << "Foo" << std::endl; }
};

class Bar : public Foo, private SmartDefs<Bar>
{
   public:
      DECL_SMART(Bar);
      void foo() const { std::cout << "Bar" << std::endl; }
};

template <class T>
struct Baz : private SmartDefs<Baz<T>>
{
   DECL_SMART(Baz<T>);
   void foo(const T& in) const { std::cout << in << std::endl; }
};

int main()
{
   auto foo = Foo::shared();
   auto bar = Bar::shared();
   auto baz = Baz<int>::shared();
   foo->foo();
   bar->foo();
   baz->foo(10);
   foo = bar;
   foo->foo();
}

Я проверил что-то подобное, но это все равно дает мненеоднозначная ссылка на shared():

template <class T>
struct SmartPtr : private SmartDefs<T>
{
   using SmartDefs<T>::Ptr;
   using SmartDefs<T>::UPtr;
   using SmartDefs<T>::shared;
   using SmartDefs<T>::unique;
};

class Foo : public SmartPtr<Foo> {};

1 Ответ

1 голос
/ 03 марта 2011

Не могли бы вы расширить:

Теперь это работает само по себе, но когда в картину приходит наследование, у меня возникают проблемы

Вы имеете в виду множественное наследование двух базовых классов, каждый из которых наследуется от вашего класса SmartDefs? Вы имеете в виду наследование как от класса SmartDefs, так и от базового класса, который сам наследуется от класса SmartDefs?

В любом случае, ваша проблема, конечно, не в C ++ 0x, а в общей неоднозначности базовых членов.

Пример:

#include <iostream>

struct A
{
    typedef int ret_type;
    static ret_type go(){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct B
{
    typedef int ret_type;
    static ret_type go(){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
};
struct C : public A, public B
{
    typedef int ret_type;
    static ret_type go(){ std::cout << __PRETTY_FUNCTION__ << std::endl; }
};

int main()
{
    C c;

    C::A::ret_type ago = c.A::go();
    C::B::ret_type bgo = c.B::go();
    C::ret_type    cgo = c.go();

    C::A::ret_type static_ago = C::A::go();
    C::B::ret_type static_bgo = C::B::go();
    C::ret_type    static_cgo = C::go();
}

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

Однако я не уверен, что рекомендую ваше решение вообще. Перед лицом наследования Derived::unique() вернул бы unique_ptr<Base>, если Derived не наследовал от вашего класса SmartPtr. Единственный «безопасный» способ реализовать нечто подобное - использовать виртуальную функцию Create () (в вашем случае: возможно, CreateUnique, CreateShared).

Я бы лично предпочел написать глобальный make_shared для переноса std::make_shared и написать свой make_unique в том же пространстве имен.

A::shared() // replace
make_shared<A>() // with this

A::unique() // replace
make_unique<A>() // with this

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

Редактировать: Я забыл упомянуть, что вы потеряете typedef для возвращаемого типа таким образом. Вы можете использовать type_traits, но я на самом деле считаю, что отсутствие typedef здесь есть функция. Информацию о базовых классах aboyt, таких как std :: shared_ptr и std :: unique_ptr, не нужно удалять с помощью typedef, а между auto, templates и decltype не требуется явных typedefs.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...