Я делаю личный проект, в котором я собираюсь реализовать движок 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));