Есть ли способ навязать дружбу в дереве наследования классу-помощнику, инкапсулирующему статического создателя shared_ptr? - PullRequest
0 голосов
/ 21 мая 2019

Я делаю личный проект, в котором я собираюсь реализовать движок 3D видеоигр. Я в самом начале, и я пытаюсь определить очень простой интерфейс для моего класса окна. В нем я хотел применить какой-то определенный метод создания, который возвращал базовый класс shared_ptr. Поскольку метод должен был быть статическим, наследование через абстрактный метод не могло быть применено.

Поэтому я подумал об использовании второго шаблонизированного класса, который бы получал в качестве параметра шаблона дочерний класс окна, следуя CRTP (любопытно повторяющийся шаблон шаблона). Этот класс будет вызывать конструктор PROTECTED (я хотел, чтобы он был инкапсулирован, чтобы могли существовать только shared_ptrs, предоставленные этим методом) дочернего класса и возвращал базовый класс shared_ptr. Это все хорошо, пока я не пойму, что для использования конструктора, защищенного от производного класса, недостаточно объявить друга базового класса, что в ретроспективе имеет такой смысл, так как дружба, будучи наследственной, не имеет смысла с точки зрения инкапсуляции (вы доверяете любому унаследованный класс?).

Как бы вы пошли, чтобы получить дружбу по наследству?

РЕДАКТИРОВАТЬ: Это было помечено как дубликат, что является приемлемым, из-за смутного названия. От ответа на этот вопрос я ожидал способа подружиться с шаблонным классом, который использует CRTP для создания shared_ptr унаследованного класса, переданного в указанные унаследованные классы. Основная проблема в том, что я хочу, чтобы конструктор оставался ЗАЩИЩЕННЫМ.

Я знаю, что следующий код вырожден, в конце концов, это результат экспериментов. Но я хотел знать, сталкивался ли кто-нибудь с этим и имеет ли какое-либо предложение.


#include <iostream>
#include <utility>
#include <memory>
#include <type_traits>
#include <cstdint>

class Window
{
    template<typename T> friend struct WindowCreateHelper;
public:
    virtual void getRequiredExtensions() = 0;
    virtual ~Window();

    Window(const Window &w) = delete;
    Window &operator=(Window const &w) = delete;

    Window(Window &&w) = default;
    Window &operator=(Window &&w) = default;

    inline uint32_t getArea(){return m_width * m_height;}

protected:
    Window(uint32_t width, uint32_t height)
    : m_width(width)
    , m_height(height)
    {};
    uint32_t m_width;
    uint32_t m_height;
};
using WindowPtr = std::shared_ptr<Window>;

template <typename W>
struct WindowCreateHelper
{
    static WindowPtr createWindow(uint32_t width = 600u, uint32_t height = 400u)
    {
        static_assert(std::is_base_of<Window, W>::value, "Window Create can only be used with classes of inherited from Window");
        return WindowPtr(new W(width, height));
    }
};

class InheritedWindow: public Window, public WindowCreateHelper<InheritedWindow>
{
protected:
    InheritedWindow(uint32_t width, uint32_t height)
    : Window(width, height)
    {
        std::cout << "Im being built!";
    }
public:
    void getRequiredExtensions() final
    {
        std::cout << "Hello!";
    }
};

int main()
{
    auto window = InheritedWindow::createWindow(100, 100);
    window->getRequiredExtensions();

    return 0;
}


Как и ожидалось, он жалуется на то, что конструктор защищен, потому что дружба не наследуется.

main.cpp: In instantiation of 'static WindowPtr WindowCreateHelper<T>::createWindow(uint32_t, uint32_t) [with W = InheritedWindow; WindowPtr = std::shared_ptr<Window>; uint32_t = unsigned int]':
<span class="error_line" onclick="ide.gotoLine('main.cpp',67)">main.cpp:67:36</span>:   required from here
main.cpp:46:26: error: 'InheritedWindow::InheritedWindow(uint32_t, uint32_t)' is protected within this context
         return WindowPtr(new W(width, height));
...