英文:
Capturing types from parameter pack and creating an array of unique_ptr from them
问题
- 如何编写这段代码?
template<typename... TComponents>
class ComponentContainer {
using ComponentPtr = std::unique_ptr<Component>;
public:
ComponentContainer()
{
// Initialize m_components with unique_ptrs to instances of TComponents
initializeComponents();
}
private:
std::array<ComponentPtr, sizeof...(TComponents)> m_components;
// Helper function to initialize m_components with unique_ptrs to instances of TComponents
void initializeComponents()
{
// Initialize components using a fold expression
((m_components[i] = std::make_unique<TComponents>()), ...);
}
};
- 有没有办法验证所有提供的
TComponents都是派生自Component类型?
// 使用SFINAE (Substitution Failure Is Not An Error) 检查每个TComponent是否派生自Component
template<typename... TComponents>
class ComponentContainer {
static_assert((std::is_base_of<Component, TComponents>::value && ...), "All TComponents must be derived from Component");
// ... 其余代码同上 ...
};
这段代码使用了 static_assert 来验证 TComponents 是否都派生自 Component 类型。如果有任何一个 TComponent 不满足这个条件,编译将会失败,并显示相应的错误消息。
英文:
I want to build a container class for my Component instances. Component is an abstract class and every type in the parameter pack should be derived from it.
template<typename... TComponents>
class ComponentContainer {
using ComponentPtr = unique_ptr<Component>;
public:
ComponentContainer()
{
}
private:
std::array<ComponentPtr, sizeof...(TComponents)> m_components;
};
The container should hold an array of pointers to the instances of each types provided.
So for every type there has to be m_components[i] = std::make_unique<TComponents>().
- How can I write it in code?
- Is there a way to validate that all the provided
TComponentsare derived fromComponenttype?
答案1
得分: 3
"Is there a way to validate that all the provided TComponents are derived from Component type?" can be translated to:
"是否有一种方法可以验证所有提供的 TComponents 是否都是从 Component 类型派生的?"
Please let me know if you need any further assistance.
英文:
> How can I write it in code?
You could add a constructor that moves or copies the components supplied to your array using std::make_unique:
#include <utility> // std::forward
template <typename... TComponents>
class ComponentContainer {
using ComponentPtr = std::unique_ptr<Component>;
public:
ComponentContainer() // default construct components
: m_components{std::make_unique<
std::remove_cv_t<std::remove_reference_t<TComponents>>>()...} {}
template <class... Args>
ComponentContainer(Args&&... comp) // move/copy construct components
: m_components{std::make_unique<
std::remove_cv_t<std::remove_reference_t<TComponents>>>(
std::forward<Args>(comp))...} {}
private:
std::array<ComponentPtr, sizeof...(TComponents)> m_components;
};
// deduction guide
template <class... Args>
ComponentContainer(Args...) ->
ComponentContainer<std::remove_cv_t<std::remove_reference_t<Args>>...>;
Making TComponents... part of the type can be problematic though. If an element in the array was initialized as the derived type C1, it will be odd if you later make the unique_ptr point at a C2. The template parameters and the contained object types won't match anymore.
Also, if you specify the template parameters explicitly, you may make them const, but the container itself can't mix non-const and const Component pointers, so they'll have to be non-const.
For both those reasons, you should probably not let users get non-const access to the individual unique_ptrs directly.
You could add tuple-like get member functions to get a reference to the contained type with the correct const qualification:
// helper trait to get the specific type out of a template parameter pack
template <size_t I, class... Args>
using type_in_pack_t = std::tuple_element_t<I, std::tuple<Args...>>;
// ...
template <std::size_t I>
const auto& get() const { // definite const&
return *static_cast<type_in_pack_t<I, TComponents...>*>(
m_components[I].get());
}
template <std::size_t I>
decltype(auto) get() { // non-const& / const& depending on template params
return *static_cast<type_in_pack_t<I, TComponents...>*>(
m_components[I].get());
}
It's often more useful to not make the concrete types of the components used to initialize the class template be a part of the final type. Since you store the elements in an array, the size of the array could be the template parameter instead:
template <size_t N>
class ComponentContainer {
using ComponentPtr = std::unique_ptr<Component>;
public:
ComponentContainer() = default;
template <class... Args>
ComponentContainer(Args&&... comp) // move/copy construct components
: m_components{
std::make_unique<std::remove_cv_t<std::remove_reference_t<Args>>>(
std::forward<Args>(comp))...} {}
private:
std::array<ComponentPtr, N> m_components{};
};
// deduction guide
template <class... Args>
ComponentContainer(Args...) -> ComponentContainer<sizeof...(Args)>;
> Is there a way to validate that all the provided TComponents are derived from Component type?
You'll get a compilation error if a pointer to one of the supplied TComponents (after remove_const_t) is not implicitly convertible to a Component*.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论